diff --git a/cc/playback/discardable_image_map.cc b/cc/playback/discardable_image_map.cc
index 74f830d..e4ae644 100644
--- a/cc/playback/discardable_image_map.cc
+++ b/cc/playback/discardable_image_map.cc
@@ -61,11 +61,9 @@
   DiscardableImagesMetadataCanvas(
       int width,
       int height,
-      std::vector<std::pair<DrawImage, gfx::Rect>>* image_set,
-      DiscardableImageMap::ImageToRegionMap* image_to_region)
+      std::vector<std::pair<DrawImage, gfx::Rect>>* image_set)
       : SkNWayCanvas(width, height),
         image_set_(image_set),
-        image_id_to_region_(image_to_region),
         canvas_bounds_(SkRect::MakeIWH(width, height)),
         canvas_size_(width, height) {}
 
@@ -175,18 +173,12 @@
 
     SkIRect src_irect;
     src_rect.roundOut(&src_irect);
-    gfx::Rect image_rect = SafeClampPaintRectToSize(paint_rect, canvas_size_);
-
-    Region& region = (*image_id_to_region_)[image->uniqueID()];
-    region.Union(image_rect);
-
     image_set_->push_back(std::make_pair(
         DrawImage(std::move(image), src_irect, filter_quality, matrix),
-        image_rect));
+        SafeClampPaintRectToSize(paint_rect, canvas_size_)));
   }
 
   std::vector<std::pair<DrawImage, gfx::Rect>>* image_set_;
-  DiscardableImageMap::ImageToRegionMap* image_id_to_region_;
   const SkRect canvas_bounds_;
   const gfx::Size canvas_size_;
   std::vector<SkPaint> saved_paints_;
@@ -202,7 +194,7 @@
     const gfx::Size& bounds) {
   DCHECK(all_images_.empty());
   return base::MakeUnique<DiscardableImagesMetadataCanvas>(
-      bounds.width(), bounds.height(), &all_images_, &image_id_to_region_);
+      bounds.width(), bounds.height(), &all_images_);
 }
 
 void DiscardableImageMap::EndGeneratingMetadata() {
@@ -222,11 +214,6 @@
     images->push_back(all_images_[index].first.ApplyScale(raster_scales));
 }
 
-Region DiscardableImageMap::GetRegionForImage(uint32_t image_id) const {
-  const auto& it = image_id_to_region_.find(image_id);
-  return it == image_id_to_region_.end() ? Region() : it->second;
-}
-
 DiscardableImageMap::ScopedMetadataGenerator::ScopedMetadataGenerator(
     DiscardableImageMap* image_map,
     const gfx::Size& bounds)
diff --git a/cc/playback/discardable_image_map.h b/cc/playback/discardable_image_map.h
index c773a72..0050680 100644
--- a/cc/playback/discardable_image_map.h
+++ b/cc/playback/discardable_image_map.h
@@ -5,12 +5,10 @@
 #ifndef CC_PLAYBACK_DISCARDABLE_IMAGE_MAP_H_
 #define CC_PLAYBACK_DISCARDABLE_IMAGE_MAP_H_
 
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include "cc/base/cc_export.h"
-#include "cc/base/region.h"
 #include "cc/base/rtree.h"
 #include "cc/playback/draw_image.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -28,9 +26,6 @@
 // rect and get back a list of DrawImages in that rect.
 class CC_EXPORT DiscardableImageMap {
  public:
-  // A map of SkImage id to the region for this image.
-  using ImageToRegionMap = std::unordered_map<uint32_t, Region>;
-
   class CC_EXPORT ScopedMetadataGenerator {
    public:
     ScopedMetadataGenerator(DiscardableImageMap* image_map,
@@ -51,7 +46,6 @@
   void GetDiscardableImagesInRect(const gfx::Rect& rect,
                                   const gfx::SizeF& raster_scales,
                                   std::vector<DrawImage>* images) const;
-  Region GetRegionForImage(uint32_t image_id) const;
 
  private:
   friend class ScopedMetadataGenerator;
@@ -61,8 +55,6 @@
   void EndGeneratingMetadata();
 
   std::vector<std::pair<DrawImage, gfx::Rect>> all_images_;
-  ImageToRegionMap image_id_to_region_;
-
   RTree images_rtree_;
 };
 
diff --git a/cc/playback/discardable_image_map_unittest.cc b/cc/playback/discardable_image_map_unittest.cc
index 06da1b14..1dfdb3a 100644
--- a/cc/playback/discardable_image_map_unittest.cc
+++ b/cc/playback/discardable_image_map_unittest.cc
@@ -108,8 +108,6 @@
                                                                 << y;
         EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
                   images[0].image_rect);
-        EXPECT_EQ(Region(images[0].image_rect),
-                  image_map.GetRegionForImage(images[0].image->uniqueID()));
       } else {
         EXPECT_EQ(0u, images.size()) << x << " " << y;
       }
@@ -120,28 +118,16 @@
   std::vector<PositionDrawImage> 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_EQ(Region(images[0].image_rect),
-            image_map.GetRegionForImage(images[0].image->uniqueID()));
-
   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_EQ(Region(images[1].image_rect),
-            image_map.GetRegionForImage(images[1].image->uniqueID()));
-
   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_EQ(Region(images[2].image_rect),
-            image_map.GetRegionForImage(images[2].image->uniqueID()));
-
   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);
-  EXPECT_EQ(Region(images[3].image_rect),
-            image_map.GetRegionForImage(images[3].image->uniqueID()));
 }
 
 TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) {
@@ -195,8 +181,6 @@
                                                                 << y;
         EXPECT_EQ(gfx::Rect(1024 + x * 512 + 6, y * 512 + 6, 500, 500),
                   images[0].image_rect);
-        EXPECT_EQ(Region(images[0].image_rect),
-                  image_map.GetRegionForImage(images[0].image->uniqueID()));
       } else {
         EXPECT_EQ(0u, images.size()) << x << " " << y;
       }
@@ -207,30 +191,18 @@
     std::vector<PositionDrawImage> 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]);
     EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 512 + 6, 500, 500),
               images[0].image_rect);
-    EXPECT_EQ(Region(images[0].image_rect),
-              image_map.GetRegionForImage(images[0].image->uniqueID()));
-
     EXPECT_TRUE(images[1].image == discardable_image[2][1]);
     EXPECT_EQ(gfx::Rect(1024 + 512 + 6, 2 * 512 + 6, 500, 500),
               images[1].image_rect);
-    EXPECT_EQ(Region(images[1].image_rect),
-              image_map.GetRegionForImage(images[1].image->uniqueID()));
-
     EXPECT_TRUE(images[2].image == discardable_image[2][3]);
     EXPECT_EQ(gfx::Rect(1024 + 3 * 512 + 6, 2 * 512 + 6, 500, 500),
               images[2].image_rect);
-    EXPECT_EQ(Region(images[2].image_rect),
-              image_map.GetRegionForImage(images[2].image->uniqueID()));
-
     EXPECT_TRUE(images[3].image == discardable_image[3][2]);
     EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 3 * 512 + 6, 500, 500),
               images[3].image_rect);
-    EXPECT_EQ(Region(images[3].image_rect),
-              image_map.GetRegionForImage(images[3].image->uniqueID()));
   }
 
   // Non intersecting rects
@@ -254,12 +226,6 @@
         image_map, gfx::Rect(3500, 1100, 1000, 1000));
     EXPECT_EQ(0u, images.size());
   }
-
-  // Image not present in the list.
-  {
-    sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(500, 500));
-    EXPECT_EQ(Region(), image_map.GetRegionForImage(image->uniqueID()));
-  }
 }
 
 TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) {
@@ -311,8 +277,6 @@
                                                                 << y;
         EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
                   images[0].image_rect);
-        EXPECT_EQ(Region(images[0].image_rect),
-                  image_map.GetRegionForImage(images[0].image->uniqueID()));
       } else {
         EXPECT_EQ(0u, images.size()) << x << " " << y;
       }
@@ -346,8 +310,6 @@
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
   EXPECT_EQ(gfx::Rect(0, 0, 2048, 2048), images[0].image_rect);
-  EXPECT_EQ(Region(images[0].image_rect),
-            image_map.GetRegionForImage(images[0].image->uniqueID()));
 }
 
 TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) {
@@ -403,8 +365,6 @@
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
   EXPECT_EQ(gfx::Rect(42, 42, 2006, 2006), images[0].image_rect);
-  EXPECT_EQ(Region(images[0].image_rect),
-            image_map.GetRegionForImage(images[0].image->uniqueID()));
 }
 
 TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) {
@@ -439,10 +399,6 @@
                                                            visible_rect.size());
     display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
   }
-
-  EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension),
-            image_map.GetRegionForImage(discardable_image->uniqueID()));
-
   std::vector<PositionDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
   EXPECT_EQ(1u, images.size());
@@ -506,15 +462,6 @@
   images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 500, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_EQ(gfx::Rect(0, 500, 1000, 100), images[0].image_rect);
-
-  Region discardable_image_region;
-  discardable_image_region.Union(gfx::Rect(0, 0, 90, 89));
-  discardable_image_region.Union(gfx::Rect(950, 951, 50, 49));
-  EXPECT_EQ(discardable_image_region,
-            image_map.GetRegionForImage(discardable_image->uniqueID()));
-
-  EXPECT_EQ(Region(gfx::Rect(0, 500, 1000, 100)),
-            image_map.GetRegionForImage(long_discardable_image->uniqueID()));
 }
 
 }  // namespace cc
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index 42011949..068cb00 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -271,8 +271,4 @@
   image_map_.GetDiscardableImagesInRect(rect, raster_scales, images);
 }
 
-Region DisplayItemList::GetRegionForImage(uint32_t image_id) {
-  return image_map_.GetRegionForImage(image_id);
-}
-
 }  // namespace cc
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
index 2a1c1d1..a18c0e2 100644
--- a/cc/playback/display_item_list.h
+++ b/cc/playback/display_item_list.h
@@ -168,7 +168,6 @@
   void GetDiscardableImagesInRect(const gfx::Rect& rect,
                                   const gfx::SizeF& raster_scales,
                                   std::vector<DrawImage>* images);
-  Region GetRegionForImage(uint32_t image_id);
 
   void SetRetainVisualRectsForTesting(bool retain) {
     retain_visual_rects_ = retain;
diff --git a/cc/playback/raster_source.h b/cc/playback/raster_source.h
index d040bcf..c158094 100644
--- a/cc/playback/raster_source.h
+++ b/cc/playback/raster_source.h
@@ -133,15 +133,6 @@
     image_decode_cache_ = image_decode_cache;
   }
 
-  // Returns the ImageDecodeCache, currently only used by
-  // GpuRasterBufferProvider in order to create its own ImageHijackCanvas.
-  // Because of the MultiPictureDraw approach used by GPU raster, it does not
-  // integrate well with the use of the ImageHijackCanvas internal to this
-  // class. See gpu_raster_buffer_provider.cc for more information.
-  // TODO(crbug.com/628394): Redesign this to avoid exposing
-  // ImageDecodeCache from the raster source.
-  ImageDecodeCache* image_decode_cache() const { return image_decode_cache_; }
-
  protected:
   friend class base::RefCountedThreadSafe<RasterSource>;
 
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 4cfe51a..9bc475d 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -26,17 +26,32 @@
 namespace cc {
 namespace {
 
-static sk_sp<SkPicture> PlaybackToPicture(
+static void RasterizeSource(
     const RasterSource* raster_source,
     bool resource_has_previous_content,
     const gfx::Size& resource_size,
     const gfx::Rect& raster_full_rect,
     const gfx::Rect& raster_dirty_rect,
     const gfx::SizeF& scales,
-    const RasterSource::PlaybackSettings& playback_settings) {
-  // GPU raster doesn't do low res tiles, so should always include images.
-  DCHECK(!playback_settings.skip_images);
+    const RasterSource::PlaybackSettings& playback_settings,
+    ContextProvider* context_provider,
+    ResourceProvider::ScopedWriteLockGL* resource_lock,
+    bool async_worker_context_enabled,
+    bool use_distance_field_text,
+    int msaa_sample_count) {
+  ScopedGpuRaster gpu_raster(context_provider);
 
+  ResourceProvider::ScopedSkSurfaceProvider scoped_surface(
+      context_provider, resource_lock, async_worker_context_enabled,
+      use_distance_field_text, raster_source->CanUseLCDText(),
+      raster_source->HasImpliedColorSpace(), msaa_sample_count);
+  SkSurface* sk_surface = scoped_surface.sk_surface();
+  // Allocating an SkSurface will fail after a lost context.  Pretend we
+  // rasterized, as the contents of the resource don't matter anymore.
+  if (!sk_surface)
+    return;
+
+  // Playback
   gfx::Rect playback_rect = raster_full_rect;
   if (resource_has_previous_content) {
     playback_rect.Intersect(raster_dirty_rect);
@@ -58,71 +73,8 @@
         100.0f * fraction_saved);
   }
 
-  // Play back raster_source into temp SkPicture.
-  SkPictureRecorder recorder;
-  SkCanvas* canvas =
-      recorder.beginRecording(resource_size.width(), resource_size.height());
-  canvas->save();
-
-  // The GPU image decode controller assumes that Skia is done with an image
-  // when playback is complete. However, in this case, where we play back to a
-  // picture, we don't actually finish with the images until the picture is
-  // rasterized later. This can cause lifetime issues in the GPU image decode
-  // controller. To avoid this, we disable the image hijack canvas (and image
-  // decode controller) for this playback step, instead enabling it for the
-  // later picture rasterization.
-  RasterSource::PlaybackSettings settings = playback_settings;
-  settings.use_image_hijack_canvas = false;
-  raster_source->PlaybackToCanvas(canvas, raster_full_rect, playback_rect,
-                                  scales, settings);
-  canvas->restore();
-  return recorder.finishRecordingAsPicture();
-}
-
-static void RasterizePicture(SkPicture* picture,
-                             ContextProvider* context_provider,
-                             ResourceProvider::ScopedWriteLockGL* resource_lock,
-                             bool async_worker_context_enabled,
-                             bool use_distance_field_text,
-                             bool can_use_lcd_text,
-                             bool ignore_resource_color_space,
-                             int msaa_sample_count,
-                             ImageDecodeCache* image_decode_cache,
-                             bool use_image_hijack_canvas) {
-  ScopedGpuRaster gpu_raster(context_provider);
-
-  ResourceProvider::ScopedSkSurfaceProvider scoped_surface(
-      context_provider, resource_lock, async_worker_context_enabled,
-      use_distance_field_text, can_use_lcd_text, ignore_resource_color_space,
-      msaa_sample_count);
-  SkSurface* sk_surface = scoped_surface.sk_surface();
-  // Allocating an SkSurface will fail after a lost context.  Pretend we
-  // rasterized, as the contents of the resource don't matter anymore.
-  if (!sk_surface)
-    return;
-
-  // As we did not use the image hijack canvas during the initial playback to
-  // |picture| (see PlaybackToPicture), we must enable it here if requested.
-  SkCanvas* canvas = sk_surface->getCanvas();
-  std::unique_ptr<ImageHijackCanvas> hijack_canvas;
-  if (use_image_hijack_canvas) {
-    DCHECK(image_decode_cache);
-    const SkImageInfo& info = canvas->imageInfo();
-    hijack_canvas.reset(
-        new ImageHijackCanvas(info.width(), info.height(), image_decode_cache));
-    SkIRect raster_bounds;
-    canvas->getClipDeviceBounds(&raster_bounds);
-    hijack_canvas->clipRect(SkRect::MakeFromIRect(raster_bounds));
-    hijack_canvas->setMatrix(canvas->getTotalMatrix());
-    hijack_canvas->addCanvas(canvas);
-
-    // Replace canvas with our ImageHijackCanvas which is wrapping it.
-    canvas = hijack_canvas.get();
-  }
-
-  SkMultiPictureDraw multi_picture_draw;
-  multi_picture_draw.add(canvas, picture);
-  multi_picture_draw.draw(false);
+  raster_source->PlaybackToCanvas(sk_surface->getCanvas(), raster_full_rect,
+                                  playback_rect, scales, playback_settings);
 }
 
 }  // namespace
@@ -261,21 +213,16 @@
     gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
   }
 
-  sk_sp<SkPicture> picture = PlaybackToPicture(
-      raster_source, resource_has_previous_content, resource_lock->size(),
-      raster_full_rect, raster_dirty_rect, scales, playback_settings);
-
   // Turn on distance fields for layers that have ever animated.
   bool use_distance_field_text =
       use_distance_field_text_ ||
       raster_source->ShouldAttemptToUseDistanceFieldText();
 
-  RasterizePicture(picture.get(), worker_context_provider_, resource_lock,
-                   async_worker_context_enabled_, use_distance_field_text,
-                   raster_source->CanUseLCDText(),
-                   raster_source->HasImpliedColorSpace(), msaa_sample_count_,
-                   raster_source->image_decode_cache(),
-                   playback_settings.use_image_hijack_canvas);
+  RasterizeSource(raster_source, resource_has_previous_content,
+                  resource_lock->size(), raster_full_rect, raster_dirty_rect,
+                  scales, playback_settings, worker_context_provider_,
+                  resource_lock, async_worker_context_enabled_,
+                  use_distance_field_text, msaa_sample_count_);
 
   const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index ade3609..87584d65 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -957,8 +957,8 @@
     "net/wake_on_wifi_connection_observer.h",
     "net/wake_on_wifi_manager.cc",
     "net/wake_on_wifi_manager.h",
-    "note_taking_app_utils.cc",
-    "note_taking_app_utils.h",
+    "note_taking_helper.cc",
+    "note_taking_helper.h",
     "options/cert_library.cc",
     "options/cert_library.h",
     "options/network_config_view.cc",
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index b9b4a980..bfc1d752 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -65,6 +65,7 @@
 #include "chrome/browser/chromeos/net/network_pref_state_observer.h"
 #include "chrome/browser/chromeos/net/network_throttling_observer.h"
 #include "chrome/browser/chromeos/net/wake_on_wifi_manager.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/chromeos/options/cert_library.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -486,6 +487,8 @@
 
   media::SoundsManager::Create();
 
+  NoteTakingHelper::Initialize();
+
   AccessibilityManager::Initialize();
 
   if (!chrome::IsRunningInMash()) {
@@ -847,6 +850,8 @@
   if (!chrome::IsRunningInMash())
     MagnificationManager::Shutdown();
 
+  NoteTakingHelper::Shutdown();
+
   media::SoundsManager::Shutdown();
 
   system::StatisticsProvider::GetInstance()->Shutdown();
diff --git a/chrome/browser/chromeos/note_taking_app_utils.h b/chrome/browser/chromeos/note_taking_app_utils.h
deleted file mode 100644
index 1f32610..0000000
--- a/chrome/browser/chromeos/note_taking_app_utils.h
+++ /dev/null
@@ -1,27 +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_CHROMEOS_NOTE_TAKING_APP_UTILS_H_
-#define CHROME_BROWSER_CHROMEOS_NOTE_TAKING_APP_UTILS_H_
-
-class Profile;
-
-namespace base {
-class FilePath;
-}  // namespace base
-
-namespace chromeos {
-
-// Returns true if an app that can be used to take notes is available.
-bool IsNoteTakingAppAvailable(Profile* profile);
-
-// Launches the note-taking app to create a new note, optionally additionally
-// passing a file (|path| may be empty). IsNoteTakingAppAvailable() must be
-// called first.
-void LaunchNoteTakingAppForNewNote(Profile* profile,
-                                   const base::FilePath& path);
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_NOTE_TAKING_APP_UTILS_H_
diff --git a/chrome/browser/chromeos/note_taking_app_utils.cc b/chrome/browser/chromeos/note_taking_helper.cc
similarity index 79%
rename from chrome/browser/chromeos/note_taking_app_utils.cc
rename to chrome/browser/chromeos/note_taking_helper.cc
index 7f12a8d..32b18a8e3 100644
--- a/chrome/browser/chromeos/note_taking_app_utils.cc
+++ b/chrome/browser/chromeos/note_taking_helper.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 
 #include <string>
 #include <vector>
@@ -25,6 +25,9 @@
 namespace chromeos {
 namespace {
 
+// Pointer to singleton instance.
+NoteTakingHelper* g_helper = nullptr;
+
 // TODO(derat): Add more IDs.
 const char* const kExtensionIds[] = {
     // TODO(jdufault): Remove testing version after m54. See crbug.com/640828.
@@ -67,13 +70,32 @@
 
 }  // namespace
 
-bool IsNoteTakingAppAvailable(Profile* profile) {
+// static
+void NoteTakingHelper::Initialize() {
+  DCHECK(!g_helper);
+  g_helper = new NoteTakingHelper();
+}
+
+// static
+void NoteTakingHelper::Shutdown() {
+  DCHECK(g_helper);
+  delete g_helper;
+  g_helper = nullptr;
+}
+
+// static
+NoteTakingHelper* NoteTakingHelper::Get() {
+  DCHECK(g_helper);
+  return g_helper;
+}
+
+bool NoteTakingHelper::IsAppAvailable(Profile* profile) {
   DCHECK(profile);
   return ash::IsPaletteFeatureEnabled() && GetApp(profile);
 }
 
-void LaunchNoteTakingAppForNewNote(Profile* profile,
-                                   const base::FilePath& path) {
+void NoteTakingHelper::LaunchAppForNewNote(Profile* profile,
+                                           const base::FilePath& path) {
   DCHECK(profile);
   const extensions::Extension* app = GetApp(profile);
   if (!app) {
@@ -86,4 +108,8 @@
   apps::LaunchPlatformAppWithAction(profile, app, std::move(action_data), path);
 }
 
+NoteTakingHelper::NoteTakingHelper() {}
+
+NoteTakingHelper::~NoteTakingHelper() {}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/note_taking_helper.h b/chrome/browser/chromeos/note_taking_helper.h
new file mode 100644
index 0000000..f8175e8
--- /dev/null
+++ b/chrome/browser/chromeos/note_taking_helper.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 CHROME_BROWSER_CHROMEOS_NOTE_TAKING_HELPER_H_
+#define CHROME_BROWSER_CHROMEOS_NOTE_TAKING_HELPER_H_
+
+#include <base/macros.h>
+
+class Profile;
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace chromeos {
+
+// Singleton class used to launch a note-taking app.
+class NoteTakingHelper {
+ public:
+  static void Initialize();
+  static void Shutdown();
+  static NoteTakingHelper* Get();
+
+  // Returns true if an app that can be used to take notes is available. UI
+  // surfaces that call LaunchAppForNewNote() should be hidden otherwise.
+  bool IsAppAvailable(Profile* profile);
+
+  // Launches the note-taking app to create a new note, optionally additionally
+  // passing a file (|path| may be empty). IsAppAvailable() must be called
+  // first.
+  void LaunchAppForNewNote(Profile* profile, const base::FilePath& path);
+
+ private:
+  NoteTakingHelper();
+  ~NoteTakingHelper();
+
+  DISALLOW_COPY_AND_ASSIGN(NoteTakingHelper);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_NOTE_TAKING_HELPER_H_
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc
index 21a618c8..38d68c58 100644
--- a/chrome/browser/download/download_commands.cc
+++ b/chrome/browser/download/download_commands.cc
@@ -37,7 +37,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace {
@@ -357,7 +357,7 @@
     case ANNOTATE:
 #if defined(OS_CHROMEOS)
       if (DownloadItemModel(download_item_).HasSupportedImageMimeType()) {
-        chromeos::LaunchNoteTakingAppForNewNote(
+        chromeos::NoteTakingHelper::Get()->LaunchAppForNewNote(
             Profile::FromBrowserContext(download_item_->GetBrowserContext()),
             download_item_->GetTargetFilePath());
       }
diff --git a/chrome/browser/download/notification/download_item_notification.cc b/chrome/browser/download/notification/download_item_notification.cc
index dfaed464..4ed7ee1b 100644
--- a/chrome/browser/download/notification/download_item_notification.cc
+++ b/chrome/browser/download/notification/download_item_notification.cc
@@ -46,7 +46,7 @@
 #include "ui/message_center/message_center_style.h"
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #endif  // defined(OS_CHROMEOS)
 
 using base::UserMetricsAction;
@@ -655,7 +655,7 @@
       if (!notification_->image().IsEmpty()) {
         actions->push_back(DownloadCommands::COPY_TO_CLIPBOARD);
 #if defined(OS_CHROMEOS)
-        if (chromeos::IsNoteTakingAppAvailable(profile()))
+        if (chromeos::NoteTakingHelper::Get()->IsAppAvailable(profile()))
           actions->push_back(DownloadCommands::ANNOTATE);
 #endif  // defined(OS_CHROMEOS)
       }
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index f9e70369..e5490fd3 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
@@ -58,6 +59,7 @@
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_set.h"
+#include "net/url_request/url_request_file_job.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/chromeos_switches.h"
@@ -70,6 +72,39 @@
 using extensions::Manifest;
 using extensions::ScopedTestDialogAutoConfirm;
 
+namespace {
+
+// Maps all chrome-extension://<id>/_test_resources/foo requests to
+// chrome/test/data/extensions/foo. This is what allows us to share code between
+// tests without needing to duplicate files in each extension.
+net::URLRequestJob* ExtensionProtocolTestHandler(
+    const base::FilePath& test_dir_root,
+    net::URLRequest* request,
+    net::NetworkDelegate* network_delegate,
+    const base::FilePath& relative_path) {
+  // Only map paths that begin with _test_resources.
+  if (!base::FilePath(FILE_PATH_LITERAL("_test_resources"))
+           .IsParent(relative_path)) {
+    return nullptr;
+  }
+
+  // Replace _test_resources/foo with chrome/test/data/foo.
+  std::vector<base::FilePath::StringType> components;
+  relative_path.GetComponents(&components);
+  DCHECK_GT(components.size(), 1u);
+  base::FilePath resource_path = test_dir_root;
+  for (size_t i = 1u; i < components.size(); ++i)
+    resource_path = resource_path.Append(components[i]);
+
+  return new net::URLRequestFileJob(
+      request, network_delegate, resource_path,
+      content::BrowserThread::GetBlockingPool()
+          ->GetTaskRunnerWithShutdownBehavior(
+              base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+}
+
+}  // namespace
+
 ExtensionBrowserTest::ExtensionBrowserTest()
     : loaded_(false),
       installed_(false),
@@ -153,11 +188,22 @@
     extension_service()->updater()->SetExtensionCacheForTesting(
         test_extension_cache_.get());
   }
+
+  // We don't use test_data_dir_ here because we want this to point to
+  // chrome/test/data/extensions, and subclasses have a nasty habit of altering
+  // the data dir in SetUpCommandLine().
+  base::FilePath test_root_path;
+  PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
+  test_root_path = test_root_path.AppendASCII("extensions");
+  test_protocol_handler_ =
+      base::Bind(&ExtensionProtocolTestHandler, test_root_path);
+  extensions::SetExtensionProtocolTestHandler(&test_protocol_handler_);
 }
 
 void ExtensionBrowserTest::TearDownOnMainThread() {
   ExtensionMessageBubbleFactory::set_override_for_tests(
       ExtensionMessageBubbleFactory::NO_OVERRIDE);
+  extensions::SetExtensionProtocolTestHandler(nullptr);
   InProcessBrowserTest::TearDownOnMainThread();
 }
 
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 57315dd..55eecc6 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -8,9 +8,9 @@
 #include <string>
 
 #include "base/command_line.h"
-
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
 #include "base/test/scoped_path_override.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
@@ -19,6 +19,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_protocols.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/feature_switch.h"
@@ -391,6 +392,12 @@
 
   // Cache cache implementation.
   std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache_;
+
+  // An override so that chrome-extensions://<extension_id>/_test_resources/foo
+  // maps to chrome/test/data/extensions/foo.
+  extensions::ExtensionProtocolTestHandler test_protocol_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionBrowserTest);
 };
 
 #endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_BROWSERTEST_H_
diff --git a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
index 25b261d..896d1614 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac_unittest.mm
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_features.h"
 
 TEST(GlobalKeyboardShortcuts, ShortcutsToWindowCommand) {
   // Test that an invalid shortcut translates into an invalid command id.
@@ -44,10 +45,17 @@
   EXPECT_EQ(IDC_SELECT_PREVIOUS_TAB, CommandForWindowKeyboardShortcut(
       true, false, false, true, kVK_ANSI_8, '{'));
 
+  // On MacViews the IDC_SELECT_TAB_0 accelerator is mapped via the
+  // accelerator_table.cc, which supports mapping using only using keycodes.
+  // The only reason CommandForWindowKeyboardShortcut is necessary on MacViews
+  // is to handle the Cmd-'{' and Cmd-'}', and it doesn't need to handle
+  // IDC_SELECT_TAB_0, so this test could be omitted.
+#if !BUILDFLAG(MAC_VIEWS_BROWSER)
   // Test that switching tabs triggers off keycodes and not characters (visible
   // with the Italian keyboard layout).
   EXPECT_EQ(IDC_SELECT_TAB_0, CommandForWindowKeyboardShortcut(
       true, false, false, false, kVK_ANSI_1, '&'));
+#endif  // !BUILDFLAG(MAC_VIEWS_BROWSER)
 }
 
 TEST(GlobalKeyboardShortcuts, KeypadNumberKeysMatch) {
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
index 37a9bd4..9ba9b8ac 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/open_util.h"
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/notifications/notifier_state_tracker.h"
@@ -136,8 +136,9 @@
         break;
       }
       case BUTTON_ANNOTATE: {
-        if (chromeos::IsNoteTakingAppAvailable(profile_)) {
-          chromeos::LaunchNoteTakingAppForNewNote(profile_, screenshot_path_);
+        chromeos::NoteTakingHelper* helper = chromeos::NoteTakingHelper::Get();
+        if (helper->IsAppAvailable(profile_)) {
+          helper->LaunchAppForNewNote(profile_, screenshot_path_);
           content::RecordAction(base::UserMetricsAction("Screenshot_Annotate"));
         }
         break;
@@ -437,7 +438,7 @@
         IDR_NOTIFICATION_SCREENSHOT_COPY_TO_CLIPBOARD);
     optional_field.buttons.push_back(copy_button);
 
-    if (chromeos::IsNoteTakingAppAvailable(GetProfile())) {
+    if (chromeos::NoteTakingHelper::Get()->IsAppAvailable(GetProfile())) {
       message_center::ButtonInfo annotate_button(l10n_util::GetStringUTF16(
           IDS_MESSAGE_CENTER_NOTIFICATION_BUTTON_ANNOTATE_SCREENSHOT));
       annotate_button.icon =
diff --git a/chrome/browser/ui/ash/palette_delegate_chromeos.cc b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
index c5ee2db..fce6545 100644
--- a/chrome/browser/ui/ash/palette_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
@@ -11,7 +11,7 @@
 #include "ash/shell.h"
 #include "ash/utility/screenshot_controller.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -57,14 +57,15 @@
   if (!profile_)
     return;
 
-  chromeos::LaunchNoteTakingAppForNewNote(profile_, base::FilePath());
+  chromeos::NoteTakingHelper::Get()->LaunchAppForNewNote(profile_,
+                                                         base::FilePath());
 }
 
 bool PaletteDelegateChromeOS::HasNoteApp() {
   if (!profile_)
     return false;
 
-  return chromeos::IsNoteTakingAppAvailable(profile_);
+  return chromeos::NoteTakingHelper::Get()->IsAppAvailable(profile_);
 }
 
 void PaletteDelegateChromeOS::ActiveUserChanged(const AccountId& account_id) {
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 82ea9c09..533d56cd 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
@@ -20,7 +20,7 @@
 #include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state_aura.h"
-#include "chrome/browser/chromeos/note_taking_app_utils.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h"
diff --git a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/framework.js b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.html b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.html
index ebd903f..dd27a43 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/test_clientRedirect.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_clientRedirect.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crash/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crash/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crash/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crash/test_crash.html b/chrome/test/data/extensions/api_test/webnavigation/crash/test_crash.html
index 31265147..4bfe7069 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crash/test_crash.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crash/test_crash.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crash.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcess/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.html b/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.html
index c6241acb..ed6003d 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcess/test_crossProcess.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crossProcess.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/framework.js
deleted file mode 100644
index 71ea900..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/framework.js
+++ /dev/null
@@ -1,263 +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.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.html b/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.html
index 76d653a..68f801c 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessAbort/test_crossProcessAbort.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crossProcessAbort.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.html b/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.html
index 16331471..6f8dfe7 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessFragment/test_crossProcessFragment.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crossProcessFragment.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.html b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.html
index 949ea22..1437012 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crossProcessHistory.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/framework.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/framework.js
deleted file mode 100644
index 71ea900..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/framework.js
+++ /dev/null
@@ -1,263 +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.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.html b/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.html
index f2d0a82d..c01ad6b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessIframe/test_crossProcessIframe.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_crossProcessIframe.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/download/framework.js b/chrome/test/data/extensions/api_test/webnavigation/download/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/download/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.html b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.html
index addcd058..bc1e0ae8 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_download.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/failures/framework.js b/chrome/test/data/extensions/api_test/webnavigation/failures/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/failures/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.html b/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.html
index f7d31a7..7858cf5 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/failures/test_failures.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_failures.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/filtered/framework.js b/chrome/test/data/extensions/api_test/webnavigation/filtered/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/filtered/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.html b/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.html
index 9661f3b..3f57ebb 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/filtered/test_filtered.html
@@ -1,2 +1 @@
 <script src="test_filtered.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/framework.js b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.html b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.html
index 7359408..51e442c7 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/test_forwardBack.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_forwardBack.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/api/framework.js b/chrome/test/data/extensions/api_test/webnavigation/framework.js
similarity index 100%
rename from chrome/test/data/extensions/api_test/webnavigation/api/framework.js
rename to chrome/test/data/extensions/api_test/webnavigation/framework.js
diff --git a/chrome/test/data/extensions/api_test/webnavigation/getFrame/framework.js b/chrome/test/data/extensions/api_test/webnavigation/getFrame/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/getFrame/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/history/framework.js b/chrome/test/data/extensions/api_test/webnavigation/history/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/history/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/history/test_history.html b/chrome/test/data/extensions/api_test/webnavigation/history/test_history.html
index 605cf9e..3d84721 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/history/test_history.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/history/test_history.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_history.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/framework.js b/chrome/test/data/extensions/api_test/webnavigation/iframe/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html
index 6ab718c2..f6bcf25 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_iframe.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/framework.js b/chrome/test/data/extensions/api_test/webnavigation/openTab/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html
index 4f06a0e..e2e321e 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_openTab.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/prerender/framework.js b/chrome/test/data/extensions/api_test/webnavigation/prerender/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/prerender/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.html b/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.html
index 42ff210..19cafb5 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/prerender/test_prerender.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_prerender.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/framework.js b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html
index 82926cb4..cda19c13 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_referenceFragment.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/framework.js b/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.html b/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.html
index 5619e37d8..f29e200e 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/requestOpenTab/test_requestOpenTab.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_requestOpenTab.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/framework.js b/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/test_serverRedirect.html b/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/test_serverRedirect.html
index e01ee2e..105ea212 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/test_serverRedirect.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/serverRedirect/test_serverRedirect.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_serverRedirect.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/framework.js b/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/test_serverRedirectSingleProcess.html b/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/test_serverRedirectSingleProcess.html
index 7e4e038..83cc854 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/test_serverRedirectSingleProcess.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/serverRedirectSingleProcess/test_serverRedirectSingleProcess.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_serverRedirectSingleProcess.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/framework.js b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.html b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.html
index c7b61e83..fca338d 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/test_simpleLoad.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_simpleLoad.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/framework.js b/chrome/test/data/extensions/api_test/webnavigation/srcdoc/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.html b/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.html
index b0e84e7..be7193b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/srcdoc/test_srcdoc.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_srcdoc.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/framework.js b/chrome/test/data/extensions/api_test/webnavigation/targetBlank/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.html b/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.html
index 3b5f1e5..24f9c5b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/targetBlank/test_targetBlank.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_targetBlank.js"></script>
-<script src="framework.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/userAction/framework.js b/chrome/test/data/extensions/api_test/webnavigation/userAction/framework.js
deleted file mode 100644
index 4a8b2e42..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/userAction/framework.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var deepEq = chrome.test.checkDeepEq;
-var expectedEventData;
-var expectedEventOrder;
-var capturedEventData;
-var nextFrameId;
-var frameIds;
-var nextTabId;
-var tabIds;
-var nextProcessId;
-var processIds;
-var initialized = false;
-
-var debug = false;
-
-function deepCopy(obj) {
-  if (obj === null)
-    return null;
-  if (typeof(obj) != 'object')
-    return obj;
-  if (Array.isArray(obj)) {
-    var tmp_array = new Array;
-    for (var i = 0; i < obj.length; i++) {
-      tmp_array.push(deepCopy(obj[i]));
-    }
-    return tmp_array;
-  }
-
-  var tmp_object = {}
-  for (var p in obj) {
-    tmp_object[p] = deepCopy(obj[p]);
-  }
-  return tmp_object;
-}
-
-// data: array of expected events, each one is a dictionary:
-//     { label: "<unique identifier>",
-//       event: "<webnavigation event type>",
-//       details: { <expected details of the event> }
-//     }
-// order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
-//     event with label "a" needs to occur before event with label "b". The
-//     relative order of "a" and "d" does not matter.
-function expect(data, order) {
-  expectedEventData = data;
-  capturedEventData = [];
-  expectedEventOrder = order;
-  nextFrameId = 1;
-  frameIds = {};
-  nextTabId = 0;
-  tabIds = {};
-  nextProcessId = -1;
-  processIds = {}
-  initListeners();
-}
-
-function checkExpectations() {
-  if (capturedEventData.length < expectedEventData.length) {
-    return;
-  }
-  if (capturedEventData.length > expectedEventData.length) {
-    chrome.test.fail("Recorded too many events. " +
-        JSON.stringify(capturedEventData));
-  }
-  // We have ensured that capturedEventData contains exactly the same elements
-  // as expectedEventData. Now we need to verify the ordering.
-  // Step 1: build positions such that
-  //     position[<event-label>]=<position of this event in capturedEventData>
-  var curPos = 0;
-  var positions = {};
-  capturedEventData.forEach(function (event) {
-    chrome.test.assertTrue(event.hasOwnProperty("label"));
-    positions[event.label] = curPos;
-    curPos++;
-  });
-  // Step 2: check that elements arrived in correct order
-  expectedEventOrder.forEach(function (order) {
-    var previousLabel = undefined;
-    order.forEach(function (label) {
-      if (previousLabel === undefined) {
-        previousLabel = label;
-        return;
-      }
-      chrome.test.assertTrue(positions[previousLabel] < positions[label],
-          "Event " + previousLabel + " is supposed to arrive before " +
-          label + ".");
-      previousLabel = label;
-    });
-  });
-  chrome.test.succeed();
-}
-
-function captureEvent(name, details) {
-  if ('url' in details) {
-    // Skip about:blank navigations
-    if (details.url == 'about:blank') {
-      return;
-    }
-    // Strip query parameter as it is hard to predict.
-    details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
-  }
-  // normalize details.
-  if ('timeStamp' in details) {
-    details.timeStamp = 0;
-  }
-  if (('frameId' in details) && (details.frameId != 0)) {
-    if (frameIds[details.frameId] === undefined) {
-      frameIds[details.frameId] = nextFrameId++;
-    }
-    details.frameId = frameIds[details.frameId];
-  }
-  if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
-    if (frameIds[details.parentFrameId] === undefined) {
-      frameIds[details.parentFrameId] = nextFrameId++;
-    }
-    details.parentFrameId = frameIds[details.parentFrameId];
-  }
-  if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
-    if (frameIds[details.sourceFrameId] === undefined) {
-      frameIds[details.sourceFrameId] = nextFrameId++;
-    }
-    details.sourceFrameId = frameIds[details.sourceFrameId];
-  }
-  if ('tabId' in details) {
-    if (tabIds[details.tabId] === undefined) {
-      tabIds[details.tabId] = nextTabId++;
-    }
-    details.tabId = tabIds[details.tabId];
-  }
-  if ('sourceTabId' in details) {
-    if (tabIds[details.sourceTabId] === undefined) {
-      tabIds[details.sourceTabId] = nextTabId++;
-    }
-    details.sourceTabId = tabIds[details.sourceTabId];
-  }
-  if ('replacedTabId' in details) {
-    if (tabIds[details.replacedTabId] === undefined) {
-      tabIds[details.replacedTabId] = nextTabId++;
-    }
-    details.replacedTabId = tabIds[details.replacedTabId];
-  }
-  if ('processId' in details) {
-    if (processIds[details.processId] === undefined) {
-      processIds[details.processId] = nextProcessId++;
-    }
-    details.processId = processIds[details.processId];
-  }
-  if ('sourceProcessId' in details) {
-    if (processIds[details.sourceProcessId] === undefined) {
-      processIds[details.sourceProcessId] = nextProcessId++;
-    }
-    details.sourceProcessId = processIds[details.sourceProcessId];
-  }
-
-  if (debug)
-    console.log("Received event '" + name + "':" + JSON.stringify(details));
-
-  // find |details| in expectedEventData
-  var found = false;
-  var label = undefined;
-  expectedEventData.forEach(function (exp) {
-    if (exp.event == name) {
-      var exp_details;
-      var alt_details;
-      if ('transitionQualifiers' in exp.details) {
-        var idx = exp.details['transitionQualifiers'].indexOf(
-            'maybe_client_redirect');
-        if (idx >= 0) {
-          exp_details = deepCopy(exp.details);
-          exp_details['transitionQualifiers'].splice(idx, 1);
-          alt_details = deepCopy(exp_details);
-          alt_details['transitionQualifiers'].push('client_redirect');
-        } else {
-          exp_details = exp.details;
-          alt_details = exp.details;
-        }
-      } else {
-        exp_details = exp.details;
-        alt_details = exp.details;
-      }
-      if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
-        if (!found) {
-          found = true;
-          label = exp.label;
-          exp.event = undefined;
-        }
-      }
-    }
-  });
-  if (!found) {
-    chrome.test.fail("Received unexpected event '" + name + "':" +
-        JSON.stringify(details));
-  }
-  capturedEventData.push({label: label, event: name, details: details});
-  checkExpectations();
-}
-
-function initListeners() {
-  if (initialized)
-    return;
-  initialized = true;
-  chrome.webNavigation.onBeforeNavigate.addListener(
-      function(details) {
-    captureEvent("onBeforeNavigate", details);
-  });
-  chrome.webNavigation.onCommitted.addListener(
-      function(details) {
-    captureEvent("onCommitted", details);
-  });
-  chrome.webNavigation.onDOMContentLoaded.addListener(
-      function(details) {
-    captureEvent("onDOMContentLoaded", details);
-  });
-  chrome.webNavigation.onCompleted.addListener(
-      function(details) {
-    captureEvent("onCompleted", details);
-  });
-  chrome.webNavigation.onCreatedNavigationTarget.addListener(
-      function(details) {
-    captureEvent("onCreatedNavigationTarget", details);
-  });
-  chrome.webNavigation.onReferenceFragmentUpdated.addListener(
-      function(details) {
-    captureEvent("onReferenceFragmentUpdated", details);
-  });
-  chrome.webNavigation.onErrorOccurred.addListener(
-      function(details) {
-    captureEvent("onErrorOccurred", details);
-  });
-  chrome.webNavigation.onTabReplaced.addListener(
-      function(details) {
-    captureEvent("onTabReplaced", details);
-  });
-  chrome.webNavigation.onHistoryStateUpdated.addListener(
-      function(details) {
-    captureEvent("onHistoryStateUpdated", details);
-  });
-}
-
-// Returns the usual order of navigation events.
-function navigationOrder(prefix) {
-  return [ prefix + "onBeforeNavigate",
-           prefix + "onCommitted",
-           prefix + "onDOMContentLoaded",
-           prefix + "onCompleted" ];
-}
-
-// Returns the constraints expressing that a frame is an iframe of another
-// frame.
-function isIFrameOf(iframe, main_frame) {
-  return [ main_frame + "onCommitted",
-           iframe + "onBeforeNavigate",
-           iframe + "onCompleted",
-           main_frame + "onCompleted" ];
-}
-
-// Returns the constraint expressing that a frame was loaded by another.
-function isLoadedBy(target, source) {
-  return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];
-}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.html b/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.html
index b78d498..7d436437 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.html
+++ b/chrome/test/data/extensions/api_test/webnavigation/userAction/test_userAction.html
@@ -1,2 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
 <script src="test_userAction.js"></script>
-<script src="framework.js"></script>
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 2542a91a7f..62fb17a4 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -95,6 +95,7 @@
 class Resource;
 struct InputEventData;
 struct PPP_Instance_Combined;
+struct URLResponseInfoData;
 class ScopedPPVar;
 }
 
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index c231083..5efe573 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -73,6 +73,8 @@
 namespace extensions {
 namespace {
 
+ExtensionProtocolTestHandler* g_test_handler = nullptr;
+
 class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
  public:
   GeneratedBackgroundPageJob(net::URLRequest* request,
@@ -523,6 +525,14 @@
       return NULL;
     }
   }
+
+  if (g_test_handler) {
+    net::URLRequestJob* test_job =
+        g_test_handler->Run(request, network_delegate, relative_path);
+    if (test_job)
+      return test_job;
+  }
+
   ContentVerifyJob* verify_job = NULL;
   ContentVerifier* verifier = extension_info_map_->content_verifier();
   if (verifier) {
@@ -590,4 +600,8 @@
                                                     extension_info_map);
 }
 
+void SetExtensionProtocolTestHandler(ExtensionProtocolTestHandler* handler) {
+  g_test_handler = handler;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/extension_protocols.h b/extensions/browser/extension_protocols.h
index aff38b1..97a7e607 100644
--- a/extensions/browser/extension_protocols.h
+++ b/extensions/browser/extension_protocols.h
@@ -8,20 +8,28 @@
 #include <memory>
 #include <string>
 
+#include "base/callback.h"
 #include "net/url_request/url_request_job_factory.h"
 
 namespace base {
+class FilePath;
 class Time;
 }
 
 namespace net {
+class URLRequest;
+class URLRequestJob;
 class HttpResponseHeaders;
 }
 
 namespace extensions {
-
 class InfoMap;
 
+using ExtensionProtocolTestHandler =
+    base::Callback<net::URLRequestJob*(net::URLRequest*,
+                                       net::NetworkDelegate*,
+                                       const base::FilePath&)>;
+
 // Builds HTTP headers for an extension request. Hashes the time to avoid
 // exposing the exact user installation time of the extension.
 net::HttpResponseHeaders* BuildHttpHeaders(
@@ -35,6 +43,11 @@
 std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>
 CreateExtensionProtocolHandler(bool is_incognito, InfoMap* extension_info_map);
 
+// Allows tests to set a special handler for chrome-extension:// urls. Note
+// that this goes through all the normal security checks; it's essentially a
+// way to map extra resources to be included in extensions.
+void SetExtensionProtocolTestHandler(ExtensionProtocolTestHandler* handler);
+
 }  // namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_EXTENSION_PROTOCOLS_H_
diff --git a/media/base/media_observer.h b/media/base/media_observer.h
index 101badf..ce81e3b 100644
--- a/media/base/media_observer.h
+++ b/media/base/media_observer.h
@@ -20,6 +20,10 @@
   virtual void OnEnteredFullscreen() = 0;
   virtual void OnExitedFullscreen() = 0;
 
+  // Called when the media element starts/stops being the dominant visible
+  // content.
+  virtual void OnBecameDominantVisibleContent(bool is_dominant) {}
+
   // Called when CDM is attached to the media element. The |cdm_context| is
   // only guaranteed to be valid in this call.
   virtual void OnSetCdm(CdmContext* cdm_context) = 0;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index cd6091d..578f7ed 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -365,6 +365,11 @@
     observer_->OnExitedFullscreen();
 }
 
+void WebMediaPlayerImpl::becameDominantVisibleContent(bool isDominant) {
+  if (observer_)
+    observer_->OnBecameDominantVisibleContent(isDominant);
+}
+
 void WebMediaPlayerImpl::DoLoad(LoadType load_type,
                                 const blink::WebURL& url,
                                 CORSMode cors_mode) {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index ab0f8e42..6e01840d 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -181,6 +181,7 @@
   bool supportsOverlayFullscreenVideo() override;
   void enteredFullscreen() override;
   void exitedFullscreen() override;
+  void becameDominantVisibleContent(bool isDominant) override;
 
   // WebMediaPlayerDelegate::Observer implementation.
   void OnHidden() override;
diff --git a/media/remoting/remoting_renderer_controller.cc b/media/remoting/remoting_renderer_controller.cc
index 72799b9f..f1abc93d 100644
--- a/media/remoting/remoting_renderer_controller.cc
+++ b/media/remoting/remoting_renderer_controller.cc
@@ -60,6 +60,13 @@
   UpdateAndMaybeSwitch();
 }
 
+void RemotingRendererController::OnBecameDominantVisibleContent(
+    bool is_dominant) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  is_dominant_content_ = is_dominant;
+  UpdateAndMaybeSwitch();
+}
+
 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -215,10 +222,10 @@
   if (is_remote_playback_disabled_)
     return false;
 
-  // Normally, entering fullscreen is the signal that starts remote rendering.
-  // However, current technical limitations require encrypted content be remoted
-  // without waiting for a user signal.
-  return is_fullscreen_;
+  // Normally, entering fullscreen or being the dominant visible content is the
+  // signal that starts remote rendering. However, current technical limitations
+  // require encrypted content be remoted without waiting for a user signal.
+  return is_fullscreen_ || is_dominant_content_;
 }
 
 void RemotingRendererController::UpdateAndMaybeSwitch() {
diff --git a/media/remoting/remoting_renderer_controller.h b/media/remoting/remoting_renderer_controller.h
index 6c7e24e..67793138 100644
--- a/media/remoting/remoting_renderer_controller.h
+++ b/media/remoting/remoting_renderer_controller.h
@@ -35,6 +35,7 @@
   // MediaObserver implementations.
   void OnEnteredFullscreen() override;
   void OnExitedFullscreen() override;
+  void OnBecameDominantVisibleContent(bool is_dominant) override;
   void OnSetCdm(CdmContext* cdm_context) override;
   void OnMetadataChanged(const PipelineMetadata& metadata) override;
   void OnRemotePlaybackDisabled(bool disabled) override;
@@ -113,6 +114,9 @@
   // https://w3c.github.io/remote-playback
   bool is_remote_playback_disabled_ = true;
 
+  // Indicates whether video is the dominant visible content in the tab.
+  bool is_dominant_content_ = false;
+
   // The callback to switch the media renderer.
   base::Closure switch_renderer_cb_;
 
diff --git a/ppapi/cpp/dev/file_chooser_dev.h b/ppapi/cpp/dev/file_chooser_dev.h
index 4cd4b28..b31c7386 100644
--- a/ppapi/cpp/dev/file_chooser_dev.h
+++ b/ppapi/cpp/dev/file_chooser_dev.h
@@ -16,7 +16,6 @@
 
 namespace pp {
 
-class CompletionCallback;
 class FileRef;
 class InstanceHandle;
 class Var;
diff --git a/ppapi/cpp/dev/video_decoder_client_dev.h b/ppapi/cpp/dev/video_decoder_client_dev.h
index a6e2bb53..5335e66b 100644
--- a/ppapi/cpp/dev/video_decoder_client_dev.h
+++ b/ppapi/cpp/dev/video_decoder_client_dev.h
@@ -12,7 +12,6 @@
 namespace pp {
 
 class Instance;
-class VideoDecoder_Dev;
 
 // This class provides a C++ interface for callbacks related to video decoding.
 // It is the C++ counterpart to PPP_VideoDecoder_Dev.
diff --git a/ppapi/cpp/instance.h b/ppapi/cpp/instance.h
index 9bc16fb..1101f51 100644
--- a/ppapi/cpp/instance.h
+++ b/ppapi/cpp/instance.h
@@ -23,8 +23,6 @@
 #undef PostMessage
 #endif
 
-struct PP_InputEvent;
-
 /// The C++ interface to the Pepper API.
 namespace pp {
 
diff --git a/ppapi/cpp/network_monitor.h b/ppapi/cpp/network_monitor.h
index c2dac4cd..f6cfbfd 100644
--- a/ppapi/cpp/network_monitor.h
+++ b/ppapi/cpp/network_monitor.h
@@ -12,7 +12,6 @@
 
 namespace pp {
 
-class Instance;
 class NetworkList;
 
 template <typename T> class CompletionCallbackWithOutput;
diff --git a/ppapi/cpp/private/ext_crx_file_system_private.h b/ppapi/cpp/private/ext_crx_file_system_private.h
index 2bd43f5..19f1b1e1 100644
--- a/ppapi/cpp/private/ext_crx_file_system_private.h
+++ b/ppapi/cpp/private/ext_crx_file_system_private.h
@@ -14,8 +14,6 @@
 
 namespace pp {
 
-class CompletionCallback;
-
 class ExtCrxFileSystemPrivate {
  public:
   ExtCrxFileSystemPrivate();
diff --git a/ppapi/cpp/private/isolated_file_system_private.h b/ppapi/cpp/private/isolated_file_system_private.h
index 2cbdc11..3915df10 100644
--- a/ppapi/cpp/private/isolated_file_system_private.h
+++ b/ppapi/cpp/private/isolated_file_system_private.h
@@ -15,8 +15,6 @@
 
 namespace pp {
 
-class CompletionCallback;
-
 class IsolatedFileSystemPrivate {
  public:
   IsolatedFileSystemPrivate();
diff --git a/ppapi/cpp/private/pdf.h b/ppapi/cpp/private/pdf.h
index 6635596..a4f309fc9 100644
--- a/ppapi/cpp/private/pdf.h
+++ b/ppapi/cpp/private/pdf.h
@@ -15,7 +15,6 @@
 
 namespace pp {
 
-class ImageData;
 class InstanceHandle;
 class Var;
 
diff --git a/ppapi/cpp/text_input_controller.h b/ppapi/cpp/text_input_controller.h
index 69b1120..8c34c73 100644
--- a/ppapi/cpp/text_input_controller.h
+++ b/ppapi/cpp/text_input_controller.h
@@ -19,7 +19,6 @@
 namespace pp {
 
 class Rect;
-class Instance;
 
 /// This class can be used for giving hints to the browser about the text input
 /// status of plugins.
diff --git a/ppapi/host/host_factory.h b/ppapi/host/host_factory.h
index 0beaa4c..947e537a 100644
--- a/ppapi/host/host_factory.h
+++ b/ppapi/host/host_factory.h
@@ -16,10 +16,6 @@
 
 namespace ppapi {
 
-namespace proxy {
-class ResourceMessageCallParams;
-}
-
 namespace host {
 
 class PpapiHost;
diff --git a/ppapi/host/ppapi_host.h b/ppapi/host/ppapi_host.h
index 5b21beb..d170ec0 100644
--- a/ppapi/host/ppapi_host.h
+++ b/ppapi/host/ppapi_host.h
@@ -23,7 +23,6 @@
 
 namespace proxy {
 class ResourceMessageCallParams;
-class ResourceMessageReplyParams;
 class SerializedHandle;
 }
 
diff --git a/ppapi/proxy/pdf_resource.h b/ppapi/proxy/pdf_resource.h
index 36736da..2c314a6c 100644
--- a/ppapi/proxy/pdf_resource.h
+++ b/ppapi/proxy/pdf_resource.h
@@ -17,8 +17,6 @@
 namespace ppapi {
 namespace proxy {
 
-class PluginDispatcher;
-
 class PPAPI_PROXY_EXPORT PDFResource
     : public PluginResource,
       public thunk::PPB_PDF_API {
diff --git a/ppapi/proxy/plugin_resource.h b/ppapi/proxy/plugin_resource.h
index 0fabab2..ad81e01 100644
--- a/ppapi/proxy/plugin_resource.h
+++ b/ppapi/proxy/plugin_resource.h
@@ -26,8 +26,6 @@
 namespace ppapi {
 namespace proxy {
 
-class PluginDispatcher;
-
 class PPAPI_PROXY_EXPORT PluginResource : public Resource {
  public:
   enum Destination {
diff --git a/ppapi/proxy/ppb_var_deprecated_proxy.h b/ppapi/proxy/ppb_var_deprecated_proxy.h
index b5a6d185..508b492 100644
--- a/ppapi/proxy/ppb_var_deprecated_proxy.h
+++ b/ppapi/proxy/ppb_var_deprecated_proxy.h
@@ -19,7 +19,6 @@
 namespace ppapi {
 namespace proxy {
 
-class SerializedVar;
 class SerializedVarReceiveInput;
 class SerializedVarVectorOutParam;
 class SerializedVarVectorReceiveInput;
diff --git a/ppapi/proxy/ppp_instance_proxy.h b/ppapi/proxy/ppp_instance_proxy.h
index 235617f..7eda2bd 100644
--- a/ppapi/proxy/ppp_instance_proxy.h
+++ b/ppapi/proxy/ppp_instance_proxy.h
@@ -16,8 +16,6 @@
 #include "ppapi/shared_impl/host_resource.h"
 #include "ppapi/shared_impl/ppp_instance_combined.h"
 
-struct PP_Rect;
-
 namespace ppapi {
 
 struct URLResponseInfoData;
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 9e7ca263..b7b0c3138 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -22,8 +22,6 @@
 
 namespace ppapi {
 
-class HostResource;
-
 namespace proxy {
 
 struct Connection;
diff --git a/ppapi/proxy/serialized_flash_menu.h b/ppapi/proxy/serialized_flash_menu.h
index 3160b26..c643f1c2 100644
--- a/ppapi/proxy/serialized_flash_menu.h
+++ b/ppapi/proxy/serialized_flash_menu.h
@@ -18,10 +18,6 @@
 
 struct PP_Flash_Menu;
 
-namespace IPC {
-class Message;
-}
-
 namespace ppapi {
 namespace proxy {
 
diff --git a/ppapi/proxy/tcp_socket_resource_base.h b/ppapi/proxy/tcp_socket_resource_base.h
index c60ff33..1df1c583 100644
--- a/ppapi/proxy/tcp_socket_resource_base.h
+++ b/ppapi/proxy/tcp_socket_resource_base.h
@@ -24,7 +24,6 @@
 
 class PPB_X509Certificate_Fields;
 class PPB_X509Certificate_Private_Shared;
-class SocketOptionData;
 
 namespace proxy {
 
diff --git a/ppapi/proxy/url_loader_resource.h b/ppapi/proxy/url_loader_resource.h
index d349beea..ce2bb322 100644
--- a/ppapi/proxy/url_loader_resource.h
+++ b/ppapi/proxy/url_loader_resource.h
@@ -19,6 +19,9 @@
 #include "ppapi/thunk/ppb_url_loader_api.h"
 
 namespace ppapi {
+
+struct URLResponseInfoData;
+
 namespace proxy {
 
 class URLResponseInfoResource;
diff --git a/ppapi/proxy/video_capture_resource.h b/ppapi/proxy/video_capture_resource.h
index 9b1a8a1..5a0e7f8 100644
--- a/ppapi/proxy/video_capture_resource.h
+++ b/ppapi/proxy/video_capture_resource.h
@@ -17,6 +17,8 @@
 namespace ppapi {
 namespace proxy {
 
+class PluginDispatcher;
+
 class VideoCaptureResource
     : public PluginResource,
       public ::ppapi::thunk::PPB_VideoCapture_API {
diff --git a/ppapi/proxy/video_decoder_resource.h b/ppapi/proxy/video_decoder_resource.h
index f8ba1757..adb3f05 100644
--- a/ppapi/proxy/video_decoder_resource.h
+++ b/ppapi/proxy/video_decoder_resource.h
@@ -30,7 +30,6 @@
 
 namespace ppapi {
 
-class PPB_Graphics3D_Shared;
 class TrackedCallback;
 
 namespace proxy {
diff --git a/ppapi/proxy/video_encoder_resource.h b/ppapi/proxy/video_encoder_resource.h
index b7106d88..59877d36 100644
--- a/ppapi/proxy/video_encoder_resource.h
+++ b/ppapi/proxy/video_encoder_resource.h
@@ -29,7 +29,6 @@
 
 namespace proxy {
 
-class SerializedHandle;
 class VideoFrameResource;
 
 class PPAPI_PROXY_EXPORT VideoEncoderResource
diff --git a/ppapi/shared_impl/ppb_var_shared.h b/ppapi/shared_impl/ppb_var_shared.h
index b1076e0..b2aba60 100644
--- a/ppapi/shared_impl/ppb_var_shared.h
+++ b/ppapi/shared_impl/ppb_var_shared.h
@@ -11,8 +11,6 @@
 #include "ppapi/c/ppb_var_array_buffer.h"
 #include "ppapi/shared_impl/ppapi_shared_export.h"
 
-struct PP_Var;
-
 namespace ppapi {
 
 class PPAPI_SHARED_EXPORT PPB_Var_Shared {
diff --git a/ppapi/tests/test_file_io.h b/ppapi/tests/test_file_io.h
index c7fc08f6..a0fb702 100644
--- a/ppapi/tests/test_file_io.h
+++ b/ppapi/tests/test_file_io.h
@@ -12,7 +12,6 @@
 #include "ppapi/tests/test_case.h"
 
 namespace pp {
-class FileIO;
 class FileSystem;
 }  // namespace pp
 
diff --git a/ppapi/tests/test_message_loop.h b/ppapi/tests/test_message_loop.h
index 29fa64c..0fb81ad 100644
--- a/ppapi/tests/test_message_loop.h
+++ b/ppapi/tests/test_message_loop.h
@@ -11,10 +11,6 @@
 #include "ppapi/tests/test_case.h"
 #include "ppapi/utility/completion_callback_factory.h"
 
-namespace pp {
-class MessageLoop;
-}
-
 class TestMessageLoop : public TestCase {
  public:
   explicit TestMessageLoop(TestingInstance* instance);
diff --git a/ppapi/tests/test_url_request.h b/ppapi/tests/test_url_request.h
index 922b1b0..22d89d0 100644
--- a/ppapi/tests/test_url_request.h
+++ b/ppapi/tests/test_url_request.h
@@ -14,10 +14,6 @@
 #include "ppapi/c/ppb_var.h"
 #include "ppapi/tests/test_case.h"
 
-namespace pp {
-class FileRef;
-}
-
 class TestURLRequest : public TestCase {
  public:
   explicit TestURLRequest(TestingInstance* instance);
diff --git a/ppapi/thunk/ppb_instance_api.h b/ppapi/thunk/ppb_instance_api.h
index 8e77156..edae8107 100644
--- a/ppapi/thunk/ppb_instance_api.h
+++ b/ppapi/thunk/ppb_instance_api.h
@@ -43,8 +43,6 @@
 
 namespace thunk {
 
-class PPB_Flash_API;
-
 class PPB_Instance_API {
  public:
   virtual ~PPB_Instance_API() {}
diff --git a/ppapi/thunk/ppb_url_loader_api.h b/ppapi/thunk/ppb_url_loader_api.h
index 8998bed..27163b6 100644
--- a/ppapi/thunk/ppb_url_loader_api.h
+++ b/ppapi/thunk/ppb_url_loader_api.h
@@ -15,7 +15,6 @@
 
 class TrackedCallback;
 struct URLRequestInfoData;
-struct URLResponseInfoData;
 
 namespace thunk {
 
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index ae6dae6..aba7f8b 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -45,8 +45,6 @@
 namespace ppapi {
 
 struct FileRefCreateInfo;
-struct URLRequestInfoData;
-struct URLResponseInfoData;
 
 namespace thunk {
 
diff --git a/ppapi/utility/websocket/websocket_api.h b/ppapi/utility/websocket/websocket_api.h
index a8ec4b5..3fd62ea 100644
--- a/ppapi/utility/websocket/websocket_api.h
+++ b/ppapi/utility/websocket/websocket_api.h
@@ -14,7 +14,6 @@
 
 namespace pp {
 
-class CompletionCallback;
 class Instance;
 class Var;
 
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index a5e9094..444b766 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1390,30 +1390,31 @@
 crbug.com/610464 [ Linux Win7 Debug ] inspector/components/throttler.html [ Failure Pass ]
 
 # TODO(jlebel): Remove when methods are implemented.
-crbug.com/624019 [ Mac ] bluetooth/notifications/add-multiple-event-listeners.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/concurrent-stops.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/event-after-starting.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/gc-with-pending-stop.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/parallel-start-stop.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/start-before-stop-resolves.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/start-stop-start-stop.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/start-succeeds.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/stop-after-start-succeeds.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/stop-twice.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/notifications/stop-without-starting.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/gen-gatt-op-device-disconnects-before.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/device-reconnects-during-success.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/reconnect-during-success.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/gen-gatt-op-device-disconnects-during-success.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/gen-gatt-op-disconnect-called-during-success.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/gen-gatt-op-disconnect-called-before.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects-with-uuid.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristic/gen-characteristic-device-disconnects-invalidates-objects.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects-with-uuid.html [ Skip ]
-crbug.com/624019 [ Mac ] bluetooth/getCharacteristic/gen-characteristic-disconnect-invalidates-objects.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/add-multiple-event-listeners.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/concurrent-stops.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/event-after-starting.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/gc-with-pending-stop.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/parallel-start-stop.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/start-before-stop-resolves.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/start-stop-start-stop.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/start-succeeds.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/stop-after-start-succeeds.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/stop-twice.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/notifications/stop-without-starting.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/device-reconnects-during-success.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/reconnect-during-success.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-during-success.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-during-success.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/characteristic/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html [ Skip ]
+crbug.com/624019 [ Mac ] bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html [ Skip ]
+
 
 
 crbug.com/487344 paint/invalidation/video-paint-invalidation.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/bluetooth/README b/third_party/WebKit/LayoutTests/bluetooth/README
index 66ef8a7..18c7f56 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/README
+++ b/third_party/WebKit/LayoutTests/bluetooth/README
@@ -4,13 +4,31 @@
 
 # generator.py
 
-The following files are generated by the generator.py script:
- - /getPrimaryService/gen-*
- - /getPrimaryServices/gen-*
+For all .js files in script-tests/, generator.py will attempt to build test
+in bluetooth/.
 
-TODO(crbug.com/654670): Generate tests for getCharacteristic(s) and
-readValue/writeValue/startNotifications.
+Note that for each subdirectory in script-test there is a matching directory
+under bluetooth/.  The generator will expand CALL functions into this
+corresponding directory.
+
+Example:
+
+bluetooth/script-tests/server/get-same-object.js expanded CALL will generate 3
+files:
+
+bluetooth/server/getPrimaryService/gen-get-same-object.html
+bluetooth/server/getPrimaryServices/gen-get-same-object.html
+bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
+
+This is because of the following lines CALL:
+
+gattServer.CALLS([
+        getPrimaryService('heart_rate')|
+        getPrimaryServices()|
+        getPrimaryServices('heart_rate')[UUID]]),
+
 
 Run
+
 $ python //third_party/WebKit/LayoutTests/bluetooth/generate.py
 to generate these files from templates in script-tests/*.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristicProperties.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/characteristicProperties.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/characteristicProperties.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/characteristicProperties.html
index 1619fd1..c5885a1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristicProperties.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/characteristicProperties.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 test(t => { assert_true(window.testRunner instanceof Object); t.done(); },
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/add-listener-after-promise.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-listener-after-promise.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/add-listener-after-promise.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-listener-after-promise.html
index dfbd2f3..a1e7c69 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/add-listener-after-promise.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-listener-after-promise.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/add-multiple-event-listeners.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-multiple-event-listeners.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/add-multiple-event-listeners.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-multiple-event-listeners.html
index 6f8116e..44b5c19 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/add-multiple-event-listeners.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/add-multiple-event-listeners.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-does-not-support-notifications.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-does-not-support-notifications.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-does-not-support-notifications.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-does-not-support-notifications.html
index 53a6fa7..423ee67 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-does-not-support-notifications.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-does-not-support-notifications.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-is-removed.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-is-removed.html
index 16b247f5..8e707ab 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/characteristic-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/characteristic-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-starts.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-starts.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-starts.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-starts.html
index 5f9b657..d9ad7ed 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-starts.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-starts.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-stops.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-stops.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-stops.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-stops.html
index 3de7f07..16906a5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/concurrent-stops.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/concurrent-stops.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/device-goes-out-of-range.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/device-goes-out-of-range.html
index 1d5add94..abd33cc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/event-after-starting.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/event-after-starting.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/event-after-starting.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/event-after-starting.html
index b2d4cde..246784c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/event-after-starting.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/event-after-starting.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-start.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-start.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-start.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-start.html
index d3db2f8..7b51df2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-start.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-start.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-stop.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-stop.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-stop.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-stop.html
index da4a8abd..2e6b6d52 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/gc-with-pending-stop.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-pending-stop.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/parallel-start-stop.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/parallel-start-stop.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/parallel-start-stop.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/parallel-start-stop.html
index bbc25d8..9efccb2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/parallel-start-stop.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/parallel-start-stop.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/service-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/service-is-removed.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/service-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/service-is-removed.html
index 6c7c47b..36f4620 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/service-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/service-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-before-stop-resolves.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-before-stop-resolves.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/start-before-stop-resolves.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-before-stop-resolves.html
index aea9ed2..5bbcb8d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-before-stop-resolves.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-before-stop-resolves.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-fails.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-fails.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/start-fails.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-fails.html
index 30645650..516b646 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-fails.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-fails.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-stop-start-stop.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/start-stop-start-stop.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
index 1e65acb..7d11a71 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-stop-start-stop.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-succeeds.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/start-succeeds.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-succeeds.html
index eff0ec8..819c611 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-succeeds.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
@@ -15,7 +15,7 @@
       return characteristic.startNotifications()
         .then(start_characteristic => {
           assert_equals(start_characteristic, characteristic,
-	                'Start characteristic should be the same as characteristic.');
+                  'Start characteristic should be the same as characteristic.');
         });
     });
   // TODO(ortuno): Assert that notifications are active.
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-twice-in-a-row.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-twice-in-a-row.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/start-twice-in-a-row.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-twice-in-a-row.html
index 60e8dfb..15cae059 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/start-twice-in-a-row.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-twice-in-a-row.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-after-start-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-after-start-succeeds.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/stop-after-start-succeeds.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-after-start-succeeds.html
index ca0832bf..33623dd6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-after-start-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-after-start-succeeds.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-twice.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-twice.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/stop-twice.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-twice.html
index 01075c7..25f5c06 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-twice.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-twice.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-without-starting.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-without-starting.html
similarity index 71%
rename from third_party/WebKit/LayoutTests/bluetooth/notifications/stop-without-starting.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-without-starting.html
index 7cbb029..3cde45e7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/notifications/stop-without-starting.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/stop-without-starting.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/add-multiple-event-listeners.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/add-multiple-event-listeners.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/add-multiple-event-listeners.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/add-multiple-event-listeners.html
index 883cbd44..116440969 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/add-multiple-event-listeners.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/add-multiple-event-listeners.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/blocklisted-characteristic.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/blocklisted-characteristic.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/blocklisted-characteristic.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/blocklisted-characteristic.html
index f1e878a..0f63d805 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/blocklisted-characteristic.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/blocklisted-characteristic.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/characteristic-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/characteristic-is-removed.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/characteristic-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/characteristic-is-removed.html
index f89e58f2..840a915 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/characteristic-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/characteristic-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/device-goes-out-of-range.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/device-goes-out-of-range.html
index ae5afa4..9bca1dd4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/event-is-fired.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/event-is-fired.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/event-is-fired.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/event-is-fired.html
index b57c1ba..d43f27d6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/event-is-fired.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/event-is-fired.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
index 9758a6c8..5b0f073 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-error.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-error.html
index 69716cdf..c5687cb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-success.html
index 1a21c56..58d37be 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-error.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-error.html
index 3bf908b..21fa221 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-success.html
index 4185d5f..c2df5ca8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-reconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
index 06e3bfd5..b5e6e29 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-error.html
index 556673c8..d0e766623 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-success.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-success.html
index 9c9a874c..4ef0264 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-error.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-error.html
index 07e9ced..bd4062c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-success.html
index 70f3c96..01d909276 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-error.html
index a9ad280..c35dbbc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-success.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-success.html
index fd66e05..718d865 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/gen-gatt-op-reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-reconnect-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-fails.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-fails.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/read-fails.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-fails.html
index caf1ffb..84e6fd27 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-fails.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-fails.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-succeeds.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/read-succeeds.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-succeeds.html
index 590948a5..3ca5e83 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-succeeds.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-updates-value.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-updates-value.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/read-updates-value.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-updates-value.html
index ee7d212..5dccc792 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/read-updates-value.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/read-updates-value.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/readValue/service-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/service-is-removed.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/readValue/service-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/service-is-removed.html
index 2102375..49d9433 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/readValue/service-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/service-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
index 66146aac..92ee117 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-error.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-error.html
index 9f454710..90369b2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-success.html
index eb7d528..fa5042b6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-error.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-error.html
index 772ddd5..67e0181a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-success.html
index f2f8fd0d..826d7368 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-reconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
index 99b78532..3720d35 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-error.html
index 1ad6c73b..a949cb3 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-success.html
index 375ccdf..16f4db8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html
index 6e2fec4..1742b77 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
index 957bbcb0..8e63831 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-error.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-error.html
index 97e8d2c2..889e301a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-success.html
index c1d2815..5486bd4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/startNotifications/gen-gatt-op-reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-reconnect-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/device-reconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/device-reconnects-during-success.html
index dab09da..7bb1385e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/device-reconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
index 50285d7f..2d9fcef 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-during-success.html
index 003b129..1dfd304 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
index f147d9d..bf00b65 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-during-success.html
index 41b2408b..aad35a2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
index 7c2c16c..0a36b80 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/reconnect-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/stopNotifications/reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/reconnect-during-success.html
index 98809e6a..219b1469 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/stopNotifications/reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/reconnect-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/blocklisted-characteristic.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/blocklisted-characteristic.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/blocklisted-characteristic.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/blocklisted-characteristic.html
index 854dc3b..4f354ac 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/blocklisted-characteristic.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/blocklisted-characteristic.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/characteristic-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/characteristic-is-removed.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/characteristic-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/characteristic-is-removed.html
index 882aa3f15..b7400f8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/characteristic-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/characteristic-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/device-goes-out-of-range.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/device-goes-out-of-range.html
index f48bf0fa..5f10320 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
index 9e2163e..f5063fc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-error.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-error.html
index a8a87fa..f82eead5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-success.html
index 212dd3d..aabfbaf 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-error.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-error.html
index 08ec020e..32057b4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-success.html
index 4a6d1e3..33e4c10 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-reconnects-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
index d004cc5..681e8fc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-error.html
index f0599b3..f1e5a2b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-success.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-success.html
index 6ea054a1..bf1a48b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html
index ff749436..184878da 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html
index dbdc4da..8a4f378 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-error.html
index 5ed0006c..3f706963 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-success.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-success.html
index c2f4f34..3b7531b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/gen-gatt-op-reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-reconnect-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 promise_test(() => {
   let val = new Uint8Array([1]);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/service-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/service-is-removed.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/service-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/service-is-removed.html
index 0125a72..e0e2f50 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/service-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/service-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/value-too-long.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/value-too-long.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/value-too-long.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/value-too-long.html
index c7790cd..e806ad7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/value-too-long.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/value-too-long.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-fails.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-fails.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/write-fails.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-fails.html
index f17621a7..72abaca 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-fails.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-fails.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-succeeds.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/write-succeeds.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-succeeds.html
index 5b37b3c1..5c27141 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-succeeds.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-updates-value.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-updates-value.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/writeValue/write-updates-value.html
rename to third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-updates-value.html
index 8746df7..f7dd018 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/writeValue/write-updates-value.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/write-updates-value.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected.html b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected.html
rename to third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected.html
index ec0eeae6..40bf913 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
  'use strict';
  promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected_gc.html b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected_gc.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected_gc.html
rename to third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected_gc.html
index 3be9852..2dffafa0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/disconnected_gc.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/disconnected_gc.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
  'use strict';
  promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/one-event-per-disconnection.html b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/one-event-per-disconnection.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/one-event-per-disconnection.html
rename to third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/one-event-per-disconnection.html
index b5ecf0b..296cd61 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/one-event-per-disconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/one-event-per-disconnection.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
  'use strict';
  promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/reconnect-during-disconnected-event.html b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/reconnect-during-disconnected-event.html
similarity index 89%
rename from third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/reconnect-during-disconnected-event.html
rename to third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/reconnect-during-disconnected-event.html
index 74d0c14..a7bbd526 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/gattserverdisconnected-event/reconnect-during-disconnected-event.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/device/gattserverdisconnected-event/reconnect-during-disconnected-event.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/generate.py b/third_party/WebKit/LayoutTests/bluetooth/generate.py
index bf13888a..127d3e4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/generate.py
+++ b/third_party/WebKit/LayoutTests/bluetooth/generate.py
@@ -24,34 +24,33 @@
 
 // script-tests/example.js
 promise_test(() => {
-  return navigator.bluetooth.requestDevice(...)
-    .then(device => device.gatt.CALLS([
-      getPrimaryService('heart_rate')|
-      getPrimaryServices('heart_rate')[UUID]]))
-    .then(device => device.gatt.PREVIOUS_CALL);
+    return navigator.bluetooth.requestDevice(...)
+        .then(device => device.gatt.CALLS([
+            getPrimaryService('heart_rate')|
+            getPrimaryServices('heart_rate')[UUID]]))
+        .then(device => device.gatt.PREVIOUS_CALL);
 }, 'example test for FUNCTION_NAME');
 
 this script will generate:
 
 // getPrimaryService/example.html
 promise_test(() => {
-  return navigator.bluetooth.requestDevice(...)
-    .then(device => device.gatt.getPrimaryService('heart_rate'))
-    .then(device => device.gatt.getPrimaryService('heart_rate'));
+    return navigator.bluetooth.requestDevice(...)
+        .then(device => device.gatt.getPrimaryService('heart_rate'))
+        .then(device => device.gatt.getPrimaryService('heart_rate'));
 }, 'example test for getPrimaryService');
 
 // getPrimaryServices/example-with-uuid.html
 promise_test(() => {
-  return navigator.bluetooth.requestDevice(...)
-    .then(device => device.gatt.getPrimaryServices('heart_rate'))
-    .then(device => device.gatt.getPrimaryServices('heart_rate'));
+    return navigator.bluetooth.requestDevice(...)
+        .then(device => device.gatt.getPrimaryServices('heart_rate'))
+        .then(device => device.gatt.getPrimaryServices('heart_rate'));
 }, 'example test for getPrimaryServices');
 
 Run
 $ python //third_party/WebKit/LayoutTests/bluetooth/generate.py
 and commit the generated files.
 """
-import glob
 import os
 import re
 import sys
@@ -61,87 +60,89 @@
 
 class GeneratedTest:
 
-  def __init__(self, data, path, template):
-    self.data = data
-    self.path = path
-    self.template = template
+    def __init__(self, data, path, template):
+        self.data = data
+        self.path = path
+        self.template = template
 
 
 def GetGeneratedTests():
-  """Yields a GeneratedTest for each call in templates in script-tests."""
-  current_path = os.path.dirname(os.path.realpath(__file__))
+    """Yields a GeneratedTest for each call in templates in script-tests."""
+    bluetooth_tests_dir = os.path.dirname(os.path.realpath(__file__))
 
-  # Read Base Test Template.
-  base_template_file_handle = open(
-      os.path.join(current_path, TEMPLATES_DIR, 'base_test_template.html'))
-  base_template_file_data = base_template_file_handle.read().decode('utf-8')
-  base_template_file_handle.close()
+    # Read Base Test Template.
+    base_template_file_handle = open(
+        os.path.join(bluetooth_tests_dir, TEMPLATES_DIR, 'base_test_template.html'))
+    base_template_file_data = base_template_file_handle.read().decode('utf-8')
+    base_template_file_handle.close()
 
-  # Get Templates.
-  available_templates = glob.glob(
-      os.path.join(current_path, TEMPLATES_DIR, '*.js'))
+    # Get Templates.
 
-  # Generate Test Files
-  for template in available_templates:
-    # Read template
-    template_file_handle = open(template)
-    template_file_data = template_file_handle.read().decode('utf-8')
-    template_file_handle.close()
+    template_path = os.path.join(bluetooth_tests_dir, TEMPLATES_DIR)
 
-    template_name = os.path.splitext(os.path.basename(template))[0]
+    available_templates = []
+    for root, _, files in os.walk(template_path):
+        for template in files:
+            if template.endswith('.js'):
+                available_templates.append(os.path.join(root, template))
 
-    result = re.search(r'CALLS\(\[(.*?)\]\)', template_file_data, re.MULTILINE
-                       | re.DOTALL)
+    # Generate Test Files
+    for template in available_templates:
+        # Read template
+        template_file_handle = open(template)
+        template_file_data = template_file_handle.read().decode('utf-8')
+        template_file_handle.close()
 
-    if result is None:
-      raise Exception('Template must contain \'CALLS\' tokens')
+        template_name = os.path.splitext(os.path.basename(template))[0]
 
-    new_test_file_data = base_template_file_data.replace('TEST',
-                                                         template_file_data)
-    # Replace CALLS([...]) with CALLS so that we don't have to replace the
-    # CALLS([...]) for every new test file.
-    new_test_file_data = new_test_file_data.replace(result.group(), 'CALLS')
+        result = re.search(r'CALLS\(\[(.*?)\]\)', template_file_data, re.MULTILINE
+            | re.DOTALL)
 
-    # Replace 'PREVIOUS_CALL' with 'CALLS' so that we can replace it while
-    # replacing CALLS.
-    new_test_file_data = new_test_file_data.replace('PREVIOUS_CALL', 'CALLS')
+        if result is None:
+            raise Exception('Template must contain \'CALLS\' tokens')
 
-    calls = result.group(1)
-    calls = ''.join(calls.split())  # Removes whitespace.
-    calls = calls.split('|')
+        new_test_file_data = base_template_file_data.replace('TEST',
+            template_file_data)
+        # Replace CALLS([...]) with CALLS so that we don't have to replace the
+        # CALLS([...]) for every new test file.
+        new_test_file_data = new_test_file_data.replace(result.group(), 'CALLS')
 
-    for call in calls:
-      # Parse call
-      name, args, uuid_suffix = re.search(r'(.*?)\((.*?)\)(\[UUID\])?',
-                                          call).groups()
+        # Replace 'PREVIOUS_CALL' with 'CALLS' so that we can replace it while
+        # replacing CALLS.
+        new_test_file_data = new_test_file_data.replace('PREVIOUS_CALL', 'CALLS')
 
-      # Replace template tokens
-      call_test_file_data = new_test_file_data
+        calls = result.group(1)
+        calls = ''.join(calls.split())    # Removes whitespace.
+        calls = calls.split('|')
 
-      call_test_file_data = call_test_file_data.replace(
-          'CALLS', '{}({})'.format(name, args))
+        for call in calls:
+            # Parse call
+            function_name, args, uuid_suffix = re.search(r'(.*?)\((.*?)\)(\[UUID\])?', call).groups()
 
-      call_test_file_data = call_test_file_data.replace('FUNCTION_NAME', name)
+            # Replace template tokens
+            call_test_file_data = new_test_file_data
+            call_test_file_data = call_test_file_data.replace('CALLS', '{}({})'.format(function_name, args))
+            call_test_file_data = call_test_file_data.replace('FUNCTION_NAME', function_name)
 
-      # Get test file name
-      call_test_file_name = 'gen-{}{}.html'.format(template_name, '-with-uuid'
-                                                   if uuid_suffix else '')
-      call_test_file_path = os.path.join(current_path, name,
-                                         call_test_file_name)
+            # Get test file name
+            group_dir = os.path.basename(os.path.abspath(os.path.join(template, os.pardir)))
 
-      yield GeneratedTest(call_test_file_data, call_test_file_path, template)
+            call_test_file_name = 'gen-{}{}.html'.format(template_name, '-with-uuid' if uuid_suffix else '')
+            call_test_file_path = os.path.join(bluetooth_tests_dir, group_dir, function_name, call_test_file_name)
+
+            yield GeneratedTest(call_test_file_data, call_test_file_path, template)
 
 
 def main():
 
-  for generated_test in GetGeneratedTests():
-    # Create or open test file
-    test_file_handle = open(generated_test.path, 'wb')
+    for generated_test in GetGeneratedTests():
+        # Create or open test file
+        test_file_handle = open(generated_test.path, 'wb')
 
-    # Write contents
-    test_file_handle.write(generated_test.data.encode('utf-8'))
-    test_file_handle.close()
+        # Write contents
+        test_file_handle.write(generated_test.data.encode('utf-8'))
+        test_file_handle.close()
 
 
 if __name__ == '__main__':
-  sys.exit(main())
+    sys.exit(main())
diff --git a/third_party/WebKit/LayoutTests/bluetooth/idl-Bluetooth.html b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-Bluetooth.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/idl-Bluetooth.html
rename to third_party/WebKit/LayoutTests/bluetooth/idl/idl-Bluetooth.html
index 4ed4ea0..82abc26 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/idl-Bluetooth.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-Bluetooth.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothDevice.html b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothDevice.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothDevice.html
rename to third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothDevice.html
index 70c8c35..45dcc9e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothDevice.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothDevice.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 test(() => {
   assert_throws(null, () => new BluetoothDevice(),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothGATTRemoteServer.html b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothGATTRemoteServer.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothGATTRemoteServer.html
rename to third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothGATTRemoteServer.html
index b7b6d65..ef976209 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothGATTRemoteServer.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothGATTRemoteServer.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
 test(() => {
   assert_throws(null, () => new BluetoothGATTRemoteServer(),
diff --git a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothUUID.html b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothUUID.html
similarity index 98%
rename from third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothUUID.html
rename to third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothUUID.html
index 9a7252e..79e35e8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/idl-BluetoothUUID.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-BluetoothUUID.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
 'use strict'
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/idl-NavigatorBluetooth.html b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-NavigatorBluetooth.html
similarity index 61%
rename from third_party/WebKit/LayoutTests/bluetooth/idl-NavigatorBluetooth.html
rename to third_party/WebKit/LayoutTests/bluetooth/idl/idl-NavigatorBluetooth.html
index e3b165c..620571ef 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/idl-NavigatorBluetooth.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/idl/idl-NavigatorBluetooth.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/base_test_template.html b/third_party/WebKit/LayoutTests/bluetooth/script-tests/base_test_template.html
index e5b6377..e4a3ad2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/base_test_template.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/base_test_template.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 TEST
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-before.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-disconnects-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-reconnects-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-reconnects-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-reconnects-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-reconnects-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-reconnects-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-reconnects-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-device-reconnects-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-reconnects-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-before.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-disconnect-called-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-garbage-collection-ran-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-garbage-collection-ran-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-garbage-collection-ran-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-garbage-collection-ran-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-garbage-collection-ran-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-garbage-collection-ran-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-reconnect-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-reconnect-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-reconnect-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-reconnect-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-reconnect-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-reconnect-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/gatt-op-reconnect-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-reconnect-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-delayed-discovery-no-permission-for-any-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/delayed-discovery-no-permission-for-any-service.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-delayed-discovery-no-permission-for-any-service.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/delayed-discovery-no-permission-for-any-service.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-before.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-device-disconnects-before.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-before.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-device-disconnects-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-device-disconnects-invalidates-objects.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-disconnect-invalidates-objects.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-garbage-collection-ran-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-garbage-collection-ran-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/garbage-collection-ran-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-get-different-service-after-reconnection.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-get-different-service-after-reconnection.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-different-service-after-reconnection.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-get-same-object.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-get-same-object.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/get-same-object.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service-no-permission-for-any-service.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/service-no-permission-for-any-service.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/server/no-permission-for-any-service.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-device-disconnects-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-device-disconnects-invalidates-objects.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-disconnect-invalidates-objects.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-garbage-collection-ran-during-error.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-garbage-collection-ran-during-success.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-success.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-garbage-collection-ran-during-success.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-success.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-get-same-object.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/get-same-object.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic-get-same-object.js
rename to third_party/WebKit/LayoutTests/bluetooth/script-tests/service/get-same-object.js
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/connect-disconnected-connect.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connect-disconnected-connect.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/connect-disconnected-connect.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/connect-disconnected-connect.html
index a3d239f..ff8565729 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/connect-disconnected-connect.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connect-disconnected-connect.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
  'use strict';
  promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/connection-fails.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-fails.html
similarity index 95%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/connection-fails.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-fails.html
index baf2c4f..d4df172 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/connection-fails.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-fails.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 // The following tests make sure the Web Bluetooth implementation
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/connection-succeeds.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
similarity index 63%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/connection-succeeds.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
index 9732181..f0d82e1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/connection-succeeds.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/connection-succeeds.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/device-goes-out-of-range.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/device-goes-out-of-range.html
index cf1c51d9..398d58d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-error.html
similarity index 68%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-error.html
index 958b3a57..29e4933 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-error.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
similarity index 68%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
index a003eda..00b5c0a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/garbage-collection-ran-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connect/get-same-gatt-server.html b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
similarity index 70%
rename from third_party/WebKit/LayoutTests/bluetooth/connect/get-same-gatt-server.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
index a5e1885..1c7774f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connect/get-same-gatt-server.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/connect/get-same-gatt-server.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/connect-disconnect-twice.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/connect-disconnect-twice.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/connect-disconnect-twice.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/connect-disconnect-twice.html
index c81996a..b6b7696 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/connect-disconnect-twice.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/connect-disconnect-twice.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/detach-gc.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/detach-gc.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/detach-gc.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/detach-gc.html
index 77144c6..cab5e4b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/detach-gc.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/detach-gc.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <body>
   <script>
   "use strict";
@@ -25,7 +25,7 @@
     setBluetoothFakeAdapter('HeartRateAdapter')
       .then(() => {
         let iframe = document.createElement('iframe');
-        iframe.src = '../../resources/bluetooth/connect-iframe.html';
+        iframe.src = '../../../resources/bluetooth/connect-iframe.html';
         document.body.appendChild(iframe);
       });
   }, 'Detach frame then garbage collect. We shouldn\'t crash.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-fires-event.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-fires-event.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-fires-event.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-fires-event.html
index fe64fb5..53e9b01 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-fires-event.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-fires-event.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
  'use strict';
  // TODO(ortuno): Write tests to check that "Disconnect" was actually
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-once.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-once.html
similarity index 73%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-once.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-once.html
index 2309ba81..5be345b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-once.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-once.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 // TODO(ortuno): Write tests to check that "Disconnect" was actually
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-twice-in-a-row.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-twice-in-a-row.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-twice-in-a-row.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-twice-in-a-row.html
index a059a96..1e6205b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/disconnect-twice-in-a-row.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/disconnect-twice-in-a-row.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect/gc-detach.html b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/gc-detach.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/disconnect/gc-detach.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/disconnect/gc-detach.html
index b6852a4..3534b67b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/disconnect/gc-detach.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/disconnect/gc-detach.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <body>
   <script>
   "use strict";
@@ -27,7 +27,7 @@
     setBluetoothFakeAdapter('HeartRateAdapter')
       .then(() => {
         let iframe = document.createElement('iframe');
-        iframe.src = '../../resources/bluetooth/connect-iframe.html';
+        iframe.src = '../../../resources/bluetooth/connect-iframe.html';
         document.body.appendChild(iframe);
       });
   }, 'Garbage collect then detach frame. We shouldn\'t crash.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-absent-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-absent-service.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-absent-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-absent-service.html
index 9d95acd..af12b6f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-absent-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-absent-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-present-service.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-present-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-present-service.html
index f9566779..7259afe 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-no-permission-present-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-found.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-found.html
index e9bc6dc18..36d595c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
index 93a68e0d..f53da55 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/delayed-discovery-service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-error.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-error.html
index dfbcdec..92e7cea9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-error.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-success.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-success.html
index 5e4db43..32d6a75 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-disconnects-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-goes-out-of-range.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-goes-out-of-range.html
index 96d99cb..3a78848 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-error.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-error.html
index 8301759..e7b8fc6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-error.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-success.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-success.html
index 5af6b47f..37113d0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/device-reconnects-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-before.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-before.html
index b9ca3ef..8710d160 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-error.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-error.html
index c7d86db0..6615258 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-error.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-success.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-success.html
index 526429f..e65a881 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnect-called-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnected-device.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnected-device.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnected-device.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnected-device.html
index 17fc2219f..4f3eb6cd 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/disconnected-device.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/disconnected-device.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-delayed-discovery-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-delayed-discovery-no-permission-for-any-service.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-delayed-discovery-no-permission-for-any-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-delayed-discovery-no-permission-for-any-service.html
index 8698fde..3e586879 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-delayed-discovery-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-delayed-discovery-no-permission-for-any-service.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-before.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-before.html
index 1ead911..c4e53f5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
index 7f06b57..ce1c1078 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-disconnect-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
index e25e64b6..36865b2b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
index b7d59fe..8805e1c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
index 84a97b0..ca80f091 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
similarity index 87%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-different-service-after-reconnection.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
index b26d83adf..f54b4c1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-same-object.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
index a00d581..03a8ad8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-get-same-object.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-no-permission-for-any-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
index 85592cf6..d806f36e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/gen-service-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/invalid-service-name.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/invalid-service-name.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/invalid-service-name.html
index f2400b9..f14172b2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/invalid-service-name.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-absent-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-absent-service.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-absent-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-absent-service.html
index f7e562f..6b7e007 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-absent-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-absent-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-present-service.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-present-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-present-service.html
index 167638b..152f5519 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/no-permission-present-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-error.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-error.html
index 468eabf..f0aebe6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-error.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-success.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-success.html
index 93dae1d..336040e4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/reconnect-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
index 1862228..e23fc14 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(function() {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
index 6c6f85e..09ab7b4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService/service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
index b0ead283..65c8b2f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
index 10e79ce12..e894ab5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/blocklisted-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/blocklisted-services.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/correct-services.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/correct-services.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/correct-services.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/correct-services.html
index f1220dc6..5228f3d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/correct-services.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/correct-services.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html
index 3042ebb2..ca2708ef 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-absent-service-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html
index f431c497..eb63d184 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service.html
index 4512960ef..10309e7d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-no-permission-present-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found-with-uuid.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found-with-uuid.html
index 5a77db7..afd2bab 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found.html
index a919045..7a4b07e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-not-found.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-not-found.html
index 64afaab..1a009f0b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
index b5a7e8e..15219a95 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-error-with-uuid.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-error-with-uuid.html
index 5c36d0a..00beaa9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-error-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success-with-uuid.html
index d4c53290..02d4a7a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success.html
index a2c2570c..eb43988f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-disconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-disconnects-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range-with-uuid.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range-with-uuid.html
index 233ed087..c536266 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range.html
index ba3ba17..9c36e912 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-error-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-error-with-uuid.html
index bb98f4f8..0127897 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-error-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success-with-uuid.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success-with-uuid.html
index a1bff072..4783179 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success.html
index 7c5a6a4c..ceb5fa4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/device-reconnects-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/device-reconnects-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before-with-uuid.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before-with-uuid.html
index 02ff61d0..deadc01e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before.html
index ffaa2b0..6b54a11 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-error-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-error-with-uuid.html
index de5a3d8d..9230211 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-error-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success-with-uuid.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success-with-uuid.html
index ef703ad..4a0393e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success.html
index aff27b3..56cd37cc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnect-called-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnect-called-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device-with-uuid.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device-with-uuid.html
index f7506d96..c7ce6ea 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device.html
similarity index 72%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device.html
index 6f1b372..ac78bc4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/disconnected-device.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/disconnected-device.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service-with-uuid.html
index 044d543d..24d961602 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service.html
index 7d406f76..cfff721 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-delayed-discovery-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-delayed-discovery-no-permission-for-any-service.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before-with-uuid.html
index 46134e7c..554bf65 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before.html
index 026a18cf..a89a8c1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-before.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(t => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
index 05aa453..fa1b568 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
index 542b18ce..60af7661 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
index 02df197..471fe2ca 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
index 439a460..d9c15cb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
index 376dda4e..77dc9bb2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
index 76658e5..c53416a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
index d3f5ab1..6bcaa648 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
index a9eb403..fb235165 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
similarity index 87%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
index 4866d58..71865c18 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
similarity index 87%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
index e8c275ae..41226bc5c4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-different-service-after-reconnection.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
index 9b4e751..5595f477 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
index 233a769..10b26f7 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-get-same-object.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
index 46c2425f..b382af76 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
index ed89818..f82e0af 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/gen-service-no-permission-for-any-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/invalid-service-name.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/invalid-service-name.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/invalid-service-name.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/invalid-service-name.html
index b74260fc..9c864b5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/invalid-service-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/invalid-service-name.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-absent-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-absent-service-with-uuid.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-absent-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-absent-service-with-uuid.html
index 9ee61b52..55dfe3d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-absent-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-absent-service-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service-with-uuid.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service-with-uuid.html
index b739f83e..e3e02a8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service.html
index f9da2e6..ea37367 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/no-permission-present-service.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/no-permission-present-service.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-error-with-uuid.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-error-with-uuid.html
index 4b98777c..2220befb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-error-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success-with-uuid.html
index f72178fff1..da81869d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success.html
index f124d879..c8ea25ad 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/reconnect-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/reconnect-during-success.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
index 3fed6a0..4b27bbb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(function() {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
index c86c76e..64c06041 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(function() {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
index a0f9687..688813e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
similarity index 74%
rename from third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
index e079b5f1..0d4b620e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryServices/services-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/blocklisted-characteristic.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/blocklisted-characteristic.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/blocklisted-characteristic.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/blocklisted-characteristic.html
index 8cc9fbc..29588f3 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/blocklisted-characteristic.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/blocklisted-characteristic.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-found.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-found.html
similarity index 84%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-found.html
index 6e55227..860f215 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
index 8d74896..b16d10f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/characteristic-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-before.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-before.html
index 99bd23b5..fdbbd88 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-during.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-during.html
index 5206c74..c2776b9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-disconnects-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-disconnects-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-goes-out-of-range.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-goes-out-of-range.html
index feb8ea5..f0650a07f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-before.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-before.html
index 465af02..154d0fe 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-during.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-during.html
index 7a3d1ddd..454402fc 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/disconnect-called-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/disconnect-called-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-device-disconnects-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
index 16c83069..6628f20 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-disconnect-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
index 3b7bbb9..801dfc1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
index b144c8a..cc4a965 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-success.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-success.html
index d25b5742..e1164cd 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-get-same-object.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-get-same-object.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-get-same-object.html
index 457241d6..9e61bcc6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/gen-characteristic-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-get-same-object.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/invalid-characteristic-name.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/invalid-characteristic-name.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/invalid-characteristic-name.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/invalid-characteristic-name.html
index ea1eada..ef93b661 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/invalid-characteristic-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/invalid-characteristic-name.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/reconnect-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/reconnect-during.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/reconnect-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/reconnect-during.html
index 289f9a4..285bac5 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/reconnect-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/reconnect-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/service-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/service-is-removed.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/service-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/service-is-removed.html
index 49614e9..64cad1e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic/service-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/service-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics-with-uuid.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics-with-uuid.html
index 11b46ff..799d8d8 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics.html
index ea01aceb..905dfdff 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/blocklisted-characteristics.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/blocklisted-characteristics.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found-with-uuid.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found-with-uuid.html
index 5abe37e8..3ae6c49b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found.html
index 6290c53..2d170d04 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
similarity index 76%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
index 1f791a5..70834546 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found.html
similarity index 75%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found.html
index d133cde..97bcc3f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/characteristics-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/correct-characteristics.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/correct-characteristics.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/correct-characteristics.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/correct-characteristics.html
index ca027874..7caf5c6 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/correct-characteristics.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/correct-characteristics.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before-with-uuid.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before-with-uuid.html
index f2b60c5..4da8cb4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before.html
index e64a7aca..74b08225 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during-with-uuid.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during-with-uuid.html
index c944b08..4333c3b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during.html
index 23b83c1..8b877bd3 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-disconnects-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-disconnects-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range-with-uuid.html
index c77fde4..524a3b4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range.html
index ed798c2..810743d1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/device-goes-out-of-range.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/device-goes-out-of-range.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before-with-uuid.html
index 6e36e492..869d486d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before.html
index f15789a..dc82293 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-before.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during-with-uuid.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during-with-uuid.html
index a039e58..fc6acff 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during.html
index 92e4e3ba..dbb551f60 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/disconnect-called-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/disconnect-called-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
index 5e161c1..2ed03f9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
index be7e39a..eeb6f22a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
index 4336bce..b7eb08f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
similarity index 90%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
index e64907c..2941a44 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
index dfb60bb..8622d25 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
index e0555959..ff88cc1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success-with-uuid.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success-with-uuid.html
index dd948a3..6e0a85c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success.html
index 3114880..58310668 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-garbage-collection-ran-during-success.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-success.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object-with-uuid.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object-with-uuid.html
index 6481d455..4d822ac 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object-with-uuid.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object.html
index b56c919..3ab308c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/gen-characteristic-get-same-object.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-get-same-object.html
@@ -1,8 +1,8 @@
 <!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/invalid-characteristic-name.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/invalid-characteristic-name.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/invalid-characteristic-name.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/invalid-characteristic-name.html
index 79aef581..d1516d8e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/invalid-characteristic-name.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/invalid-characteristic-name.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during-with-uuid.html
similarity index 81%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during-with-uuid.html
index 914c6cb..1504a5486 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during.html
index 67e013a..e7591b4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/reconnect-during.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/reconnect-during.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed-with-uuid.html
similarity index 79%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed-with-uuid.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed-with-uuid.html
index 99472ef..6d468a7c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed-with-uuid.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed.html
rename to third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed.html
index c79a712..314f2ca 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristics/service-is-removed.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/service-is-removed.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
 promise_test(() => {
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
index f2bf4f8..e4d92cd 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.h
@@ -55,8 +55,6 @@
 };
 
 class DOMObjectHolderBase;
-template <typename T>
-class DOMObjectHolder;
 
 // This class represent a collection of DOM wrappers for a specific world.
 class CORE_EXPORT DOMWrapperWorld : public RefCounted<DOMWrapperWorld> {
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.h b/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.h
index e97b8df..d58a89b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptEventListener.h
@@ -42,7 +42,6 @@
 class LocalFrame;
 class Node;
 class QualifiedName;
-class ScheduledAction;
 class SourceLocation;
 
 V8LazyEventListener* createAttributeEventListener(
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
index 43aff09..c29ad4c6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
@@ -24,7 +24,6 @@
 class DOMArrayBufferView;
 class File;
 class FileList;
-class ImageData;
 class StaticBitmapImage;
 
 typedef Vector<WTF::ArrayBufferContents, 1> ArrayBufferContentsArray;
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8ScriptRunner.h b/third_party/WebKit/Source/bindings/core/v8/V8ScriptRunner.h
index 5a4bd360..6dfbb3a0 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8ScriptRunner.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8ScriptRunner.h
@@ -41,7 +41,6 @@
 namespace blink {
 
 class CachedMetadataHandler;
-class Resource;
 class ScriptResource;
 class ScriptSourceCode;
 class ExecutionContext;
diff --git a/third_party/WebKit/Source/core/animation/ElementAnimation.h b/third_party/WebKit/Source/core/animation/ElementAnimation.h
index 9a16e58..ac4aeb61 100644
--- a/third_party/WebKit/Source/core/animation/ElementAnimation.h
+++ b/third_party/WebKit/Source/core/animation/ElementAnimation.h
@@ -46,8 +46,6 @@
 
 namespace blink {
 
-class Dictionary;
-
 class ElementAnimation {
   STATIC_ONLY(ElementAnimation);
 
diff --git a/third_party/WebKit/Source/core/css/ActiveStyleSheets.h b/third_party/WebKit/Source/core/css/ActiveStyleSheets.h
index 3078615..266f86a5 100644
--- a/third_party/WebKit/Source/core/css/ActiveStyleSheets.h
+++ b/third_party/WebKit/Source/core/css/ActiveStyleSheets.h
@@ -12,8 +12,6 @@
 
 class CSSStyleSheet;
 class RuleSet;
-class StyleEngine;
-class TreeScope;
 
 using ActiveStyleSheet = std::pair<Member<CSSStyleSheet>, Member<RuleSet>>;
 using ActiveStyleSheetVector = HeapVector<ActiveStyleSheet>;
diff --git a/third_party/WebKit/Source/core/css/resolver/FontBuilder.h b/third_party/WebKit/Source/core/css/resolver/FontBuilder.h
index ee5deda..e2943ca3 100644
--- a/third_party/WebKit/Source/core/css/resolver/FontBuilder.h
+++ b/third_party/WebKit/Source/core/css/resolver/FontBuilder.h
@@ -34,7 +34,6 @@
 
 namespace blink {
 
-class CSSValue;
 class FontSelector;
 class ComputedStyle;
 
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolver.h b/third_party/WebKit/Source/core/css/resolver/StyleResolver.h
index 172189d..e69b7256 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.h
+++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.h
@@ -50,10 +50,8 @@
 class Element;
 class Interpolation;
 class MatchResult;
-class MediaQueryEvaluator;
 class RuleSet;
 class StylePropertySet;
-class StyleRule;
 class StyleRuleUsageTracker;
 
 enum StyleSharingBehavior {
diff --git a/third_party/WebKit/Source/core/dom/BUILD.gn b/third_party/WebKit/Source/core/dom/BUILD.gn
index c2a91e0..186e895 100644
--- a/third_party/WebKit/Source/core/dom/BUILD.gn
+++ b/third_party/WebKit/Source/core/dom/BUILD.gn
@@ -155,6 +155,8 @@
     "IgnoreDestructiveWriteCountIncrementer.h",
     "IncrementLoadEventDelayCount.cpp",
     "IncrementLoadEventDelayCount.h",
+    "IntersectionGeometry.cpp",
+    "IntersectionGeometry.h",
     "IntersectionObservation.cpp",
     "IntersectionObservation.h",
     "IntersectionObserver.cpp",
diff --git a/third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp b/third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
new file mode 100644
index 0000000..1956b7b6
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
@@ -0,0 +1,241 @@
+// Copyright 2016 The Chromium Authors. 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/IntersectionGeometry.h"
+
+#include "core/dom/Element.h"
+#include "core/dom/ElementRareData.h"
+#include "core/frame/FrameView.h"
+#include "core/frame/LocalFrame.h"
+#include "core/layout/LayoutBox.h"
+#include "core/layout/LayoutView.h"
+#include "core/layout/api/LayoutAPIShim.h"
+#include "core/layout/api/LayoutViewItem.h"
+#include "core/paint/PaintLayer.h"
+
+namespace blink {
+
+namespace {
+
+bool isContainingBlockChainDescendant(LayoutObject* descendant,
+                                      LayoutObject* ancestor) {
+  LocalFrame* ancestorFrame = ancestor->document().frame();
+  LocalFrame* descendantFrame = descendant->document().frame();
+
+  if (ancestor->isLayoutView())
+    return descendantFrame && descendantFrame->tree().top() == ancestorFrame;
+
+  if (ancestorFrame != descendantFrame)
+    return false;
+
+  while (descendant && descendant != ancestor)
+    descendant = descendant->containingBlock();
+  return descendant;
+}
+
+void mapRectUpToDocument(LayoutRect& rect,
+                         const LayoutObject& layoutObject,
+                         const Document& document) {
+  FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
+      FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
+  rect = LayoutRect(mappedQuad.boundingBox());
+}
+
+void mapRectDownToDocument(LayoutRect& rect,
+                           LayoutBoxModelObject& layoutObject,
+                           const Document& document) {
+  FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
+      &layoutObject, FloatQuad(FloatRect(rect)),
+      UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
+  rect = LayoutRect(mappedQuad.boundingBox());
+}
+
+LayoutUnit computeMargin(const Length& length, LayoutUnit referenceLength) {
+  if (length.type() == Percent) {
+    return LayoutUnit(
+        static_cast<int>(referenceLength.toFloat() * length.percent() / 100.0));
+  }
+  DCHECK_EQ(length.type(), Fixed);
+  return LayoutUnit(length.intValue());
+}
+
+}  // namespace
+
+IntersectionGeometry::IntersectionGeometry(
+    Node* root,
+    Element* target,
+    const Vector<Length>& rootMargin,
+    ReportRootBounds shouldReportRootBounds)
+    : m_root(root),
+      m_target(target),
+      m_rootMargin(rootMargin),
+      m_shouldReportRootBounds(shouldReportRootBounds) {
+  DCHECK(m_target);
+  DCHECK(rootMargin.isEmpty() || rootMargin.size() == 4);
+}
+
+IntersectionGeometry::~IntersectionGeometry() {}
+
+Element* IntersectionGeometry::root() const {
+  if (m_root && !m_root->isDocumentNode())
+    return toElement(m_root);
+  return nullptr;
+}
+
+LayoutObject* IntersectionGeometry::getRootLayoutObject() const {
+  DCHECK(m_root);
+  if (m_root->isDocumentNode()) {
+    return LayoutAPIShim::layoutObjectFrom(
+        toDocument(m_root)->layoutViewItem());
+  }
+  return toElement(m_root)->layoutObject();
+}
+
+void IntersectionGeometry::initializeGeometry() {
+  initializeTargetRect();
+  m_intersectionRect = m_targetRect;
+  initializeRootRect();
+  m_doesIntersect = true;
+}
+
+void IntersectionGeometry::initializeTargetRect() {
+  LayoutObject* targetLayoutObject = m_target->layoutObject();
+  DCHECK(targetLayoutObject && targetLayoutObject->isBoxModelObject());
+  m_targetRect = LayoutRect(
+      toLayoutBoxModelObject(targetLayoutObject)->borderBoundingBox());
+}
+
+void IntersectionGeometry::initializeRootRect() {
+  LayoutObject* rootLayoutObject = getRootLayoutObject();
+  if (rootLayoutObject->isLayoutView()) {
+    m_rootRect = LayoutRect(
+        toLayoutView(rootLayoutObject)->frameView()->visibleContentRect());
+  } else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip()) {
+    m_rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
+  } else {
+    m_rootRect = LayoutRect(
+        toLayoutBoxModelObject(rootLayoutObject)->borderBoundingBox());
+  }
+  applyRootMargin();
+}
+
+void IntersectionGeometry::applyRootMargin() {
+  if (m_rootMargin.isEmpty())
+    return;
+
+  // TODO(szager): Make sure the spec is clear that left/right margins are
+  // resolved against width and not height.
+  LayoutUnit topMargin = computeMargin(m_rootMargin[0], m_rootRect.height());
+  LayoutUnit rightMargin = computeMargin(m_rootMargin[1], m_rootRect.width());
+  LayoutUnit bottomMargin = computeMargin(m_rootMargin[2], m_rootRect.height());
+  LayoutUnit leftMargin = computeMargin(m_rootMargin[3], m_rootRect.width());
+
+  m_rootRect.setX(m_rootRect.x() - leftMargin);
+  m_rootRect.setWidth(m_rootRect.width() + leftMargin + rightMargin);
+  m_rootRect.setY(m_rootRect.y() - topMargin);
+  m_rootRect.setHeight(m_rootRect.height() + topMargin + bottomMargin);
+}
+
+void IntersectionGeometry::clipToRoot() {
+  // Map and clip rect into root element coordinates.
+  // TODO(szager): the writing mode flipping needs a test.
+  LayoutBox* rootLayoutObject = toLayoutBox(getRootLayoutObject());
+  LayoutObject* targetLayoutObject = m_target->layoutObject();
+
+  m_doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
+      rootLayoutObject, m_intersectionRect, EdgeInclusive);
+  if (rootLayoutObject->hasOverflowClip())
+    m_intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
+
+  if (!m_doesIntersect)
+    return;
+  LayoutRect rootClipRect(m_rootRect);
+  rootLayoutObject->flipForWritingMode(rootClipRect);
+  m_doesIntersect &= m_intersectionRect.inclusiveIntersect(rootClipRect);
+}
+
+void IntersectionGeometry::mapTargetRectToTargetFrameCoordinates() {
+  LayoutObject& targetLayoutObject = *m_target->layoutObject();
+  Document& targetDocument = m_target->document();
+  LayoutSize scrollPosition =
+      LayoutSize(targetDocument.view()->getScrollOffset());
+  mapRectUpToDocument(m_targetRect, targetLayoutObject, targetDocument);
+  m_targetRect.move(-scrollPosition);
+}
+
+void IntersectionGeometry::mapRootRectToRootFrameCoordinates() {
+  LayoutObject& rootLayoutObject = *getRootLayoutObject();
+  Document& rootDocument = rootLayoutObject.document();
+  LayoutSize scrollPosition =
+      LayoutSize(rootDocument.view()->getScrollOffset());
+  mapRectUpToDocument(m_rootRect, rootLayoutObject,
+                      rootLayoutObject.document());
+  m_rootRect.move(-scrollPosition);
+}
+
+void IntersectionGeometry::mapRootRectToTargetFrameCoordinates() {
+  LayoutObject& rootLayoutObject = *getRootLayoutObject();
+  Document& targetDocument = m_target->document();
+  LayoutSize scrollPosition =
+      LayoutSize(targetDocument.view()->getScrollOffset());
+
+  if (&targetDocument == &rootLayoutObject.document()) {
+    mapRectUpToDocument(m_intersectionRect, rootLayoutObject, targetDocument);
+  } else {
+    mapRectDownToDocument(m_intersectionRect,
+                          toLayoutBoxModelObject(rootLayoutObject),
+                          targetDocument);
+  }
+
+  m_intersectionRect.move(-scrollPosition);
+}
+
+void IntersectionGeometry::computeGeometry() {
+  // In the first few lines here, before initializeGeometry is called, "return
+  // true" effectively means "if the previous observed state was that root and
+  // target were intersecting, then generate a notification indicating that they
+  // are no longer intersecting."  This happens, for example, when root or
+  // target is removed from the DOM tree and not reinserted before the next
+  // frame is generated, or display:none is set on the root or target.
+  if (!m_target->isConnected())
+    return;
+  Element* rootElement = root();
+  if (rootElement && !rootElement->isConnected())
+    return;
+
+  LayoutObject* rootLayoutObject = getRootLayoutObject();
+  if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject())
+    return;
+  // TODO(szager): Support SVG
+  LayoutObject* targetLayoutObject = m_target->layoutObject();
+  if (!targetLayoutObject)
+    return;
+  if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
+    return;
+  if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject))
+    return;
+
+  initializeGeometry();
+
+  clipToRoot();
+
+  mapTargetRectToTargetFrameCoordinates();
+
+  if (m_doesIntersect)
+    mapRootRectToTargetFrameCoordinates();
+  else
+    m_intersectionRect = LayoutRect();
+
+  // Small optimization: if we're not going to report root bounds, don't bother
+  // transforming them to the frame.
+  if (m_shouldReportRootBounds == ReportRootBounds::kShouldReportRootBounds)
+    mapRootRectToRootFrameCoordinates();
+}
+
+DEFINE_TRACE(IntersectionGeometry) {
+  visitor->trace(m_root);
+  visitor->trace(m_target);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/IntersectionGeometry.h b/third_party/WebKit/Source/core/dom/IntersectionGeometry.h
new file mode 100644
index 0000000..ba94456
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/IntersectionGeometry.h
@@ -0,0 +1,73 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IntersectionGeometry_h
+#define IntersectionGeometry_h
+
+#include "platform/Length.h"
+#include "platform/geometry/LayoutRect.h"
+#include "platform/heap/Handle.h"
+#include "wtf/Vector.h"
+
+namespace blink {
+
+class Node;
+class Element;
+class LayoutObject;
+
+class IntersectionGeometry final
+    : public GarbageCollectedFinalized<IntersectionGeometry> {
+ public:
+  enum ReportRootBounds {
+    kShouldReportRootBounds,
+    kShouldNotReportRootBounds,
+  };
+
+  IntersectionGeometry(Node* root,
+                       Element* target,
+                       const Vector<Length>& rootMargin,
+                       ReportRootBounds shouldReportRootBounds);
+  ~IntersectionGeometry();
+
+  void computeGeometry();
+  LayoutRect targetRect() const { return m_targetRect; }
+  LayoutRect intersectionRect() const { return m_intersectionRect; }
+  LayoutRect rootRect() const { return m_rootRect; }
+  bool doesIntersect() const { return m_doesIntersect; }
+
+  IntRect intersectionIntRect() const {
+    return pixelSnappedIntRect(m_intersectionRect);
+  }
+
+  IntRect targetIntRect() const { return pixelSnappedIntRect(m_targetRect); }
+
+  IntRect rootIntRect() const { return pixelSnappedIntRect(m_rootRect); }
+
+  DECLARE_TRACE();
+
+ private:
+  void initializeGeometry();
+  void initializeTargetRect();
+  void initializeRootRect();
+  void clipToRoot();
+  void mapTargetRectToTargetFrameCoordinates();
+  void mapRootRectToRootFrameCoordinates();
+  void mapRootRectToTargetFrameCoordinates();
+  Element* root() const;
+  LayoutObject* getRootLayoutObject() const;
+  void applyRootMargin();
+
+  Member<Node> m_root;
+  Member<Element> m_target;
+  const Vector<Length> m_rootMargin;
+  const ReportRootBounds m_shouldReportRootBounds;
+  LayoutRect m_targetRect;
+  LayoutRect m_intersectionRect;
+  LayoutRect m_rootRect;
+  bool m_doesIntersect = false;
+};
+
+}  // namespace blink
+
+#endif  // IntersectionGeometry_h
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
index 55d610a..2ae1556 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
+++ b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
@@ -6,199 +6,35 @@
 
 #include "core/dom/ElementRareData.h"
 #include "core/dom/IntersectionObserver.h"
-#include "core/frame/FrameView.h"
-#include "core/frame/LocalFrame.h"
-#include "core/layout/LayoutBox.h"
-#include "core/layout/LayoutView.h"
-#include "core/paint/PaintLayer.h"
 
 namespace blink {
 
-IntersectionObservation::IntersectionObservation(IntersectionObserver& observer,
-                                                 Element& target,
-                                                 bool shouldReportRootBounds)
+IntersectionObservation::IntersectionObservation(
+    IntersectionObserver& observer,
+    Element& target,
+    IntersectionGeometry::ReportRootBounds shouldReportRootBounds)
     : m_observer(observer),
       m_target(&target),
-      m_shouldReportRootBounds(shouldReportRootBounds),
+      m_shouldReportRootBounds(
+          shouldReportRootBounds ==
+          IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds),
       m_lastThresholdIndex(0) {}
 
-void IntersectionObservation::applyRootMargin(LayoutRect& rect) const {
-  if (m_shouldReportRootBounds)
-    m_observer->applyRootMargin(rect);
-}
-
-void IntersectionObservation::initializeGeometry(
-    IntersectionGeometry& geometry) const {
-  initializeTargetRect(geometry.targetRect);
-  geometry.intersectionRect = geometry.targetRect;
-  initializeRootRect(geometry.rootRect);
-  geometry.doesIntersect = true;
-}
-
-void IntersectionObservation::initializeTargetRect(LayoutRect& rect) const {
-  DCHECK(m_target);
-  LayoutObject* targetLayoutObject = target()->layoutObject();
-  DCHECK(targetLayoutObject && targetLayoutObject->isBoxModelObject());
-  rect = LayoutRect(
-      toLayoutBoxModelObject(targetLayoutObject)->borderBoundingBox());
-}
-
-void IntersectionObservation::initializeRootRect(LayoutRect& rect) const {
-  LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
-  if (rootLayoutObject->isLayoutView())
-    rect = LayoutRect(
-        toLayoutView(rootLayoutObject)->frameView()->visibleContentRect());
-  else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip())
-    rect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
-  else
-    rect = LayoutRect(
-        toLayoutBoxModelObject(rootLayoutObject)->borderBoundingBox());
-  applyRootMargin(rect);
-}
-
-void IntersectionObservation::clipToRoot(IntersectionGeometry& geometry) const {
-  // Map and clip rect into root element coordinates.
-  // TODO(szager): the writing mode flipping needs a test.
-  DCHECK(m_target);
-  LayoutBox* rootLayoutObject = toLayoutBox(m_observer->rootLayoutObject());
-  LayoutObject* targetLayoutObject = target()->layoutObject();
-
-  geometry.doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
-      rootLayoutObject, geometry.intersectionRect, EdgeInclusive);
-  if (rootLayoutObject->hasOverflowClip())
-    geometry.intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
-
-  if (!geometry.doesIntersect)
-    return;
-  LayoutRect rootClipRect(geometry.rootRect);
-  rootLayoutObject->flipForWritingMode(rootClipRect);
-  geometry.doesIntersect &=
-      geometry.intersectionRect.inclusiveIntersect(rootClipRect);
-}
-
-static void mapRectUpToDocument(LayoutRect& rect,
-                                const LayoutObject& layoutObject,
-                                const Document& document) {
-  FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
-      FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
-  rect = LayoutRect(mappedQuad.boundingBox());
-}
-
-static void mapRectDownToDocument(LayoutRect& rect,
-                                  LayoutBoxModelObject& layoutObject,
-                                  const Document& document) {
-  FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
-      &layoutObject, FloatQuad(FloatRect(rect)),
-      UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
-  rect = LayoutRect(mappedQuad.boundingBox());
-}
-
-void IntersectionObservation::mapTargetRectToTargetFrameCoordinates(
-    LayoutRect& rect) const {
-  LayoutObject& targetLayoutObject = *target()->layoutObject();
-  Document& targetDocument = target()->document();
-  LayoutSize scrollPosition =
-      LayoutSize(targetDocument.view()->getScrollOffset());
-  mapRectUpToDocument(rect, targetLayoutObject, targetDocument);
-  rect.move(-scrollPosition);
-}
-
-void IntersectionObservation::mapRootRectToRootFrameCoordinates(
-    LayoutRect& rect) const {
-  LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
-  Document& rootDocument = rootLayoutObject.document();
-  LayoutSize scrollPosition =
-      LayoutSize(rootDocument.view()->getScrollOffset());
-  mapRectUpToDocument(rect, rootLayoutObject, rootLayoutObject.document());
-  rect.move(-scrollPosition);
-}
-
-void IntersectionObservation::mapRootRectToTargetFrameCoordinates(
-    LayoutRect& rect) const {
-  LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
-  Document& targetDocument = target()->document();
-  LayoutSize scrollPosition =
-      LayoutSize(targetDocument.view()->getScrollOffset());
-
-  if (&targetDocument == &rootLayoutObject.document())
-    mapRectUpToDocument(rect, rootLayoutObject, targetDocument);
-  else
-    mapRectDownToDocument(rect, toLayoutBoxModelObject(rootLayoutObject),
-                          targetDocument);
-
-  rect.move(-scrollPosition);
-}
-
-static bool isContainingBlockChainDescendant(LayoutObject* descendant,
-                                             LayoutObject* ancestor) {
-  LocalFrame* ancestorFrame = ancestor->document().frame();
-  LocalFrame* descendantFrame = descendant->document().frame();
-
-  if (ancestor->isLayoutView())
-    return descendantFrame && descendantFrame->tree().top() == ancestorFrame;
-
-  if (ancestorFrame != descendantFrame)
-    return false;
-
-  while (descendant && descendant != ancestor)
-    descendant = descendant->containingBlock();
-  return descendant;
-}
-
-bool IntersectionObservation::computeGeometry(
-    IntersectionGeometry& geometry) const {
-  // In the first few lines here, before initializeGeometry is called, "return
-  // true" effectively means "if the previous observed state was that root and
-  // target were intersecting, then generate a notification indicating that they
-  // are no longer intersecting."  This happens, for example, when root or
-  // target is removed from the DOM tree and not reinserted before the next
-  // frame is generated, or display:none is set on the root or target.
-  Element* targetElement = target();
-  if (!targetElement)
-    return false;
-  if (!targetElement->isConnected())
-    return true;
-  DCHECK(m_observer);
-  Element* rootElement = m_observer->root();
-  if (rootElement && !rootElement->isConnected())
-    return true;
-
-  LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
-  if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject())
-    return true;
-  // TODO(szager): Support SVG
-  LayoutObject* targetLayoutObject = targetElement->layoutObject();
-  if (!targetLayoutObject)
-    return true;
-  if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
-    return true;
-  if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject))
-    return true;
-
-  initializeGeometry(geometry);
-
-  clipToRoot(geometry);
-
-  mapTargetRectToTargetFrameCoordinates(geometry.targetRect);
-
-  if (geometry.doesIntersect)
-    mapRootRectToTargetFrameCoordinates(geometry.intersectionRect);
-  else
-    geometry.intersectionRect = LayoutRect();
-
-  // Small optimization: if we're not going to report root bounds, don't bother
-  // transforming them to the frame.
-  if (m_shouldReportRootBounds)
-    mapRootRectToRootFrameCoordinates(geometry.rootRect);
-
-  return true;
-}
-
 void IntersectionObservation::computeIntersectionObservations(
     DOMHighResTimeStamp timestamp) {
-  IntersectionGeometry geometry;
-  if (!computeGeometry(geometry))
+  if (!m_target)
     return;
+  Vector<Length> rootMargin(4);
+  rootMargin[0] = m_observer->topMargin();
+  rootMargin[1] = m_observer->rightMargin();
+  rootMargin[2] = m_observer->bottomMargin();
+  rootMargin[3] = m_observer->leftMargin();
+  IntersectionGeometry geometry(
+      m_observer->rootNode(), target(), rootMargin,
+      m_shouldReportRootBounds
+          ? IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds
+          : IntersectionGeometry::ReportRootBounds::kShouldNotReportRootBounds);
+  geometry.computeGeometry();
 
   // Some corner cases for threshold index:
   //   - If target rect is zero area, because it has zero width and/or zero
@@ -215,27 +51,26 @@
   //       any non-zero threshold.
   unsigned newThresholdIndex;
   float newVisibleRatio = 0;
-  if (geometry.targetRect.isEmpty()) {
-    newThresholdIndex = geometry.doesIntersect ? 1 : 0;
-  } else if (!geometry.doesIntersect) {
+  if (geometry.targetRect().isEmpty()) {
+    newThresholdIndex = geometry.doesIntersect() ? 1 : 0;
+  } else if (!geometry.doesIntersect()) {
     newThresholdIndex = 0;
   } else {
     float intersectionArea =
-        geometry.intersectionRect.size().width().toFloat() *
-        geometry.intersectionRect.size().height().toFloat();
-    float targetArea = geometry.targetRect.size().width().toFloat() *
-                       geometry.targetRect.size().height().toFloat();
+        geometry.intersectionRect().size().width().toFloat() *
+        geometry.intersectionRect().size().height().toFloat();
+    float targetArea = geometry.targetRect().size().width().toFloat() *
+                       geometry.targetRect().size().height().toFloat();
     newVisibleRatio = intersectionArea / targetArea;
     newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio);
   }
   if (m_lastThresholdIndex != newThresholdIndex) {
-    IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect);
+    IntRect snappedRootBounds = geometry.rootIntRect();
     IntRect* rootBoundsPointer =
         m_shouldReportRootBounds ? &snappedRootBounds : nullptr;
     IntersectionObserverEntry* newEntry = new IntersectionObserverEntry(
-        timestamp, newVisibleRatio, pixelSnappedIntRect(geometry.targetRect),
-        rootBoundsPointer, pixelSnappedIntRect(geometry.intersectionRect),
-        target());
+        timestamp, newVisibleRatio, geometry.targetIntRect(), rootBoundsPointer,
+        geometry.intersectionIntRect(), target());
     observer().enqueueIntersectionObserverEntry(*newEntry);
     setLastThresholdIndex(newThresholdIndex);
   }
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.h b/third_party/WebKit/Source/core/dom/IntersectionObservation.h
index cd87187c..3b1ec65d 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObservation.h
+++ b/third_party/WebKit/Source/core/dom/IntersectionObservation.h
@@ -6,7 +6,7 @@
 #define IntersectionObservation_h
 
 #include "core/dom/DOMHighResTimeStamp.h"
-#include "platform/geometry/LayoutRect.h"
+#include "core/dom/IntersectionGeometry.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
@@ -17,24 +17,15 @@
 class IntersectionObservation final
     : public GarbageCollected<IntersectionObservation> {
  public:
-  IntersectionObservation(IntersectionObserver&,
-                          Element&,
-                          bool shouldReportRootBounds);
-
-  struct IntersectionGeometry {
-    LayoutRect targetRect;
-    LayoutRect intersectionRect;
-    LayoutRect rootRect;
-    bool doesIntersect;
-
-    IntersectionGeometry() : doesIntersect(false) {}
-  };
+  IntersectionObservation(
+      IntersectionObserver&,
+      Element&,
+      IntersectionGeometry::ReportRootBounds shouldReportRootBounds);
 
   IntersectionObserver& observer() const { return *m_observer; }
   Element* target() const { return m_target; }
   unsigned lastThresholdIndex() const { return m_lastThresholdIndex; }
   void setLastThresholdIndex(unsigned index) { m_lastThresholdIndex = index; }
-  bool shouldReportRootBounds() const { return m_shouldReportRootBounds; }
   void computeIntersectionObservations(DOMHighResTimeStamp);
   void disconnect();
   void clearRootAndRemoveFromTarget();
@@ -42,21 +33,10 @@
   DECLARE_TRACE();
 
  private:
-  void applyRootMargin(LayoutRect&) const;
-  void initializeGeometry(IntersectionGeometry&) const;
-  void initializeTargetRect(LayoutRect&) const;
-  void initializeRootRect(LayoutRect&) const;
-  void clipToRoot(IntersectionGeometry&) const;
-  void mapTargetRectToTargetFrameCoordinates(LayoutRect&) const;
-  void mapRootRectToRootFrameCoordinates(LayoutRect&) const;
-  void mapRootRectToTargetFrameCoordinates(LayoutRect&) const;
-  bool computeGeometry(IntersectionGeometry&) const;
-
   Member<IntersectionObserver> m_observer;
-
   WeakMember<Element> m_target;
 
-  unsigned m_shouldReportRootBounds : 1;
+  const unsigned m_shouldReportRootBounds : 1;
   unsigned m_lastThresholdIndex : 30;
 };
 
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
index 60be083..09e1dc56f 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
@@ -20,8 +20,6 @@
 #include "core/frame/LocalFrame.h"
 #include "core/html/HTMLFrameOwnerElement.h"
 #include "core/inspector/ConsoleMessage.h"
-#include "core/layout/api/LayoutAPIShim.h"
-#include "core/layout/api/LayoutViewItem.h"
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
 #include "platform/Timer.h"
@@ -243,13 +241,6 @@
   m_root = nullptr;
 }
 
-LayoutObject* IntersectionObserver::rootLayoutObject() const {
-  Node* node = rootNode();
-  if (node->isDocumentNode())
-    return LayoutAPIShim::layoutObjectFrom(toDocument(node)->layoutViewItem());
-  return toElement(node)->layoutObject();
-}
-
 void IntersectionObserver::observe(Element* target,
                                    ExceptionState& exceptionState) {
   if (!m_root) {
@@ -279,8 +270,11 @@
     isDOMDescendant = (targetFrame->tree().top() == rootFrame);
   }
 
-  IntersectionObservation* observation =
-      new IntersectionObservation(*this, *target, shouldReportRootBounds);
+  IntersectionObservation* observation = new IntersectionObservation(
+      *this, *target,
+      shouldReportRootBounds
+          ? IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds
+          : IntersectionGeometry::ReportRootBounds::kShouldNotReportRootBounds);
   target->ensureIntersectionObserverData().addObservation(*observation);
   m_observations.add(observation);
 
@@ -406,29 +400,6 @@
       .scheduleIntersectionObserverForDelivery(*this);
 }
 
-static LayoutUnit computeMargin(const Length& length,
-                                LayoutUnit referenceLength) {
-  if (length.type() == Percent)
-    return LayoutUnit(
-        static_cast<int>(referenceLength.toFloat() * length.percent() / 100.0));
-  DCHECK_EQ(length.type(), Fixed);
-  return LayoutUnit(length.intValue());
-}
-
-void IntersectionObserver::applyRootMargin(LayoutRect& rect) const {
-  // TODO(szager): Make sure the spec is clear that left/right margins are
-  // resolved against width and not height.
-  LayoutUnit topMargin = computeMargin(m_topMargin, rect.height());
-  LayoutUnit rightMargin = computeMargin(m_rightMargin, rect.width());
-  LayoutUnit bottomMargin = computeMargin(m_bottomMargin, rect.height());
-  LayoutUnit leftMargin = computeMargin(m_leftMargin, rect.width());
-
-  rect.setX(rect.x() - leftMargin);
-  rect.setWidth(rect.width() + leftMargin + rightMargin);
-  rect.setY(rect.y() - topMargin);
-  rect.setHeight(rect.height() + topMargin + bottomMargin);
-}
-
 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const {
   unsigned result = 0;
   while (result < m_thresholds.size() && m_thresholds[result] <= ratio)
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.h b/third_party/WebKit/Source/core/dom/IntersectionObserver.h
index 3281fce..cb3913595 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObserver.h
+++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.h
@@ -19,7 +19,6 @@
 class Document;
 class Element;
 class ExceptionState;
-class LayoutObject;
 class IntersectionObserverCallback;
 class IntersectionObserverInit;
 
@@ -64,14 +63,12 @@
   const Vector<float>& thresholds() const { return m_thresholds; }
 
   Node* rootNode() const { return m_root.get(); }
-  LayoutObject* rootLayoutObject() const;
   const Length& topMargin() const { return m_topMargin; }
   const Length& rightMargin() const { return m_rightMargin; }
   const Length& bottomMargin() const { return m_bottomMargin; }
   const Length& leftMargin() const { return m_leftMargin; }
   void computeIntersectionObservations();
   void enqueueIntersectionObserverEntry(IntersectionObserverEntry&);
-  void applyRootMargin(LayoutRect&) const;
   unsigned firstThresholdGreaterThan(float ratio) const;
   void deliver();
   void removeObservation(IntersectionObservation&);
diff --git a/third_party/WebKit/Source/core/dom/Node.h b/third_party/WebKit/Source/core/dom/Node.h
index bd9263f..00beb275 100644
--- a/third_party/WebKit/Source/core/dom/Node.h
+++ b/third_party/WebKit/Source/core/dom/Node.h
@@ -46,13 +46,11 @@
 
 namespace blink {
 
-class Attribute;
 class ContainerNode;
 class Document;
 class Element;
 class ElementShadow;
 class Event;
-class EventListener;
 class ExceptionState;
 class GetRootNodeOptions;
 class HTMLQualifiedName;
@@ -76,7 +74,6 @@
 using StaticNodeList = StaticNodeTypeList<Node>;
 class StyleChangeReasonForTracing;
 class Text;
-class TouchEvent;
 
 const int nodeStyleChangeShift = 18;
 const int nodeCustomElementShift = 20;
diff --git a/third_party/WebKit/Source/core/dom/ProcessingInstruction.h b/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
index d74df93..ec134b5 100644
--- a/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
+++ b/third_party/WebKit/Source/core/dom/ProcessingInstruction.h
@@ -31,7 +31,6 @@
 namespace blink {
 
 class StyleSheet;
-class CSSStyleSheet;
 class EventListener;
 
 class ProcessingInstruction final : public CharacterData,
diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.h b/third_party/WebKit/Source/core/editing/InputMethodController.h
index b2b59b5..d8bd023 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.h
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.h
@@ -43,7 +43,6 @@
 class Editor;
 class LocalFrame;
 class Range;
-class Text;
 
 class CORE_EXPORT InputMethodController final
     : public GarbageCollectedFinalized<InputMethodController>,
diff --git a/third_party/WebKit/Source/core/editing/Position.h b/third_party/WebKit/Source/core/editing/Position.h
index 0117c6d..88935d12 100644
--- a/third_party/WebKit/Source/core/editing/Position.h
+++ b/third_party/WebKit/Source/core/editing/Position.h
@@ -39,7 +39,6 @@
 namespace blink {
 
 class Node;
-class Text;
 enum class TextAffinity;
 class TreeScope;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
index 758f7c9..8a4ecdc4 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
@@ -40,7 +40,6 @@
 namespace blink {
 
 class Node;
-class Range;
 class RenderedDocumentMarker;
 class Text;
 
diff --git a/third_party/WebKit/Source/core/editing/serializers/Serialization.h b/third_party/WebKit/Source/core/editing/serializers/Serialization.h
index bc6521d..5946f37 100644
--- a/third_party/WebKit/Source/core/editing/serializers/Serialization.h
+++ b/third_party/WebKit/Source/core/editing/serializers/Serialization.h
@@ -43,7 +43,6 @@
 class Element;
 class ExceptionState;
 class Node;
-class Range;
 class StylePropertySet;
 
 enum EChildrenOnly { IncludeNode, ChildrenOnly };
diff --git a/third_party/WebKit/Source/core/events/WindowEventContext.h b/third_party/WebKit/Source/core/events/WindowEventContext.h
index 169f55b..5adb730 100644
--- a/third_party/WebKit/Source/core/events/WindowEventContext.h
+++ b/third_party/WebKit/Source/core/events/WindowEventContext.h
@@ -35,7 +35,6 @@
 
 class EventTarget;
 class Event;
-class Node;
 class NodeEventContext;
 
 class WindowEventContext : public GarbageCollected<WindowEventContext> {
diff --git a/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.h b/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.h
index c51a0d8..ac2530c 100644
--- a/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.h
+++ b/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.h
@@ -39,7 +39,6 @@
 
 using HTTPHeaderSet = HashSet<String, CaseFoldingHash>;
 
-class Resource;
 struct ResourceLoaderOptions;
 class ResourceRequest;
 class ResourceResponse;
diff --git a/third_party/WebKit/Source/core/fetch/Resource.h b/third_party/WebKit/Source/core/fetch/Resource.h
index 0533f86..f7617f1 100644
--- a/third_party/WebKit/Source/core/fetch/Resource.h
+++ b/third_party/WebKit/Source/core/fetch/Resource.h
@@ -47,7 +47,6 @@
 namespace blink {
 
 struct FetchInitiatorInfo;
-class CachedMetadata;
 class FetchRequest;
 class ResourceClient;
 class ResourceTimingInfo;
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
index 4f67343..3b73a98d 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
@@ -47,8 +47,6 @@
 namespace blink {
 
 class ArchiveResource;
-class DocumentResource;
-class ImageResource;
 class MHTMLArchive;
 class KURL;
 class ResourceTimingInfo;
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.h b/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
index c9abc022..2d0a9d2 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
@@ -43,11 +43,9 @@
 class DOMWindowEventQueue;
 class DOMWindowProperty;
 class DocumentInit;
-class EventListener;
 class EventQueue;
 class FrameConsole;
 class MessageEvent;
-class Page;
 class PostMessageTimer;
 class SecurityOrigin;
 class SourceLocation;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.h b/third_party/WebKit/Source/core/frame/LocalFrame.h
index 6b796ab2..c979b7b 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.h
@@ -70,12 +70,10 @@
 class NavigationScheduler;
 class Node;
 class NodeTraversal;
-class Performance;
 class PerformanceMonitor;
 template <typename Strategy>
 class PositionWithAffinityTemplate;
 class PluginData;
-class Range;
 class ScriptController;
 class SpellChecker;
 class WebFrameScheduler;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 6b69c4fa..49afa31 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -39,6 +39,7 @@
 #include "core/dom/ElementTraversal.h"
 #include "core/dom/ElementVisibilityObserver.h"
 #include "core/dom/Fullscreen.h"
+#include "core/dom/IntersectionGeometry.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/dom/shadow/ShadowRoot.h"
 #include "core/events/Event.h"
@@ -125,6 +126,9 @@
 
 namespace {
 
+constexpr float kMostlyFillViewportThreshold = 0.85f;
+constexpr double kMostlyFillViewportBecomeStableSeconds = 5;
+
 enum MediaControlsShow {
   MediaControlsShowAttribute = 0,
   MediaControlsShowFullscreen,
@@ -344,6 +348,9 @@
       m_playbackProgressTimer(this,
                               &HTMLMediaElement::playbackProgressTimerFired),
       m_audioTracksTimer(this, &HTMLMediaElement::audioTracksTimerFired),
+      m_viewportFillDebouncerTimer(
+          this,
+          &HTMLMediaElement::viewportFillDebouncerTimerFired),
       m_playedTimeRanges(),
       m_asyncEventQueue(GenericEventQueue::create(this)),
       m_playbackRate(1.0f),
@@ -383,6 +390,7 @@
       m_processingPreferenceChange(false),
       m_playingRemotely(false),
       m_inOverlayFullscreenVideo(false),
+      m_mostlyFillingViewport(false),
       m_audioTracks(this, AudioTrackList::create(*this)),
       m_videoTracks(this, VideoTrackList::create(*this)),
       m_textTracks(this, nullptr),
@@ -1126,6 +1134,8 @@
 
   if (isFullscreen())
     m_webMediaPlayer->enteredFullscreen();
+
+  m_webMediaPlayer->becameDominantVisibleContent(m_mostlyFillingViewport);
 }
 
 void HTMLMediaElement::setPlayerPreload() {
@@ -2408,6 +2418,8 @@
 }
 
 void HTMLMediaElement::playbackProgressTimerFired(TimerBase*) {
+  checkViewportIntersectionChanged();
+
   if (!std::isnan(m_fragmentEndTime) && currentTime() >= m_fragmentEndTime &&
       getDirectionOfPlayback() == Forward) {
     m_fragmentEndTime = std::numeric_limits<double>::quiet_NaN();
@@ -4033,4 +4045,42 @@
   visitor->trace(m_client);
 }
 
+void HTMLMediaElement::checkViewportIntersectionChanged() {
+  // TODO(xjz): Early return if we not in tab mirroring.
+
+  IntersectionGeometry geometry(
+      &document(), this, Vector<Length>(),
+      IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds);
+  geometry.computeGeometry();
+  IntRect intersectRect = geometry.intersectionIntRect();
+  if (m_currentIntersectRect == intersectRect)
+    return;
+
+  m_currentIntersectRect = intersectRect;
+  // Reset on any intersection change, since this indicates the user is
+  // scrolling around in the document, the document is changing layout, etc.
+  m_viewportFillDebouncerTimer.stop();
+  bool isMostlyFillingViewport =
+      (m_currentIntersectRect.size().area() >
+       kMostlyFillViewportThreshold * geometry.rootIntRect().size().area());
+  if (m_mostlyFillingViewport == isMostlyFillingViewport)
+    return;
+
+  if (!isMostlyFillingViewport) {
+    m_mostlyFillingViewport = isMostlyFillingViewport;
+    if (m_webMediaPlayer)
+      m_webMediaPlayer->becameDominantVisibleContent(m_mostlyFillingViewport);
+    return;
+  }
+
+  m_viewportFillDebouncerTimer.startOneShot(
+      kMostlyFillViewportBecomeStableSeconds, BLINK_FROM_HERE);
+}
+
+void HTMLMediaElement::viewportFillDebouncerTimerFired(TimerBase*) {
+  m_mostlyFillingViewport = true;
+  if (m_webMediaPlayer)
+    m_webMediaPlayer->becameDominantVisibleContent(m_mostlyFillingViewport);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index 98109b40..9ced65c9 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -333,6 +333,9 @@
   virtual void setDisplayMode(DisplayMode mode) { m_displayMode = mode; }
 
  private:
+  // Friend class for testing.
+  friend class MediaElementFillingViewportTest;
+
   void resetMediaPlayerAndMediaSource();
 
   bool alwaysCreateUserAgentShadowRoot() const final { return true; }
@@ -540,10 +543,14 @@
 
   void onVisibilityChangedForAutoplay(bool isVisible);
 
+  void checkViewportIntersectionChanged();
+  void viewportFillDebouncerTimerFired(TimerBase*);
+
   UnthrottledThreadTimer<HTMLMediaElement> m_loadTimer;
   UnthrottledThreadTimer<HTMLMediaElement> m_progressEventTimer;
   UnthrottledThreadTimer<HTMLMediaElement> m_playbackProgressTimer;
   UnthrottledThreadTimer<HTMLMediaElement> m_audioTracksTimer;
+  UnthrottledThreadTimer<HTMLMediaElement> m_viewportFillDebouncerTimer;
   Member<TimeRanges> m_playedTimeRanges;
   Member<GenericEventQueue> m_asyncEventQueue;
 
@@ -643,6 +650,8 @@
   // Whether this element is in overlay fullscreen mode.
   bool m_inOverlayFullscreenVideo : 1;
 
+  bool m_mostlyFillingViewport : 1;
+
   TraceWrapperMember<AudioTrackList> m_audioTracks;
   TraceWrapperMember<VideoTrackList> m_videoTracks;
   TraceWrapperMember<TextTrackList> m_textTracks;
@@ -724,6 +733,8 @@
   // class AutoplayVisibilityObserver;
   Member<ElementVisibilityObserver> m_autoplayVisibilityObserver;
 
+  IntRect m_currentIntersectRect;
+
   static URLRegistry* s_mediaStreamRegistry;
 };
 
diff --git a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
index 1cac8da..e2d67c6 100644
--- a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
@@ -31,7 +31,6 @@
 namespace blink {
 
 class BeforeTextInsertedEvent;
-class ExceptionState;
 
 class CORE_EXPORT HTMLTextAreaElement final : public TextControlElement {
   DEFINE_WRAPPERTYPEINFO();
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
index bd7326db..f518457 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
@@ -64,7 +64,6 @@
 class HTMLResourcePreloader;
 class HTMLScriptRunner;
 class HTMLTreeBuilder;
-class PumpSession;
 class SegmentedString;
 class TokenizedChunkQueue;
 class DocumentWriteEvaluator;
diff --git a/third_party/WebKit/Source/core/html/shadow/SliderThumbElement.h b/third_party/WebKit/Source/core/html/shadow/SliderThumbElement.h
index 52c5519e..8082e82e 100644
--- a/third_party/WebKit/Source/core/html/shadow/SliderThumbElement.h
+++ b/third_party/WebKit/Source/core/html/shadow/SliderThumbElement.h
@@ -40,6 +40,7 @@
 
 class HTMLInputElement;
 class Event;
+class TouchEvent;
 
 class SliderThumbElement final : public HTMLDivElement {
  public:
diff --git a/third_party/WebKit/Source/core/input/EventHandler.h b/third_party/WebKit/Source/core/input/EventHandler.h
index 983b92d..c2ee3de 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.h
+++ b/third_party/WebKit/Source/core/input/EventHandler.h
@@ -53,7 +53,6 @@
 
 namespace blink {
 
-class ContainerNode;
 class DataTransfer;
 class PaintLayer;
 class Element;
@@ -77,7 +76,6 @@
 class Scrollbar;
 class SelectionController;
 class TextEvent;
-class WheelEvent;
 
 class CORE_EXPORT EventHandler final
     : public GarbageCollectedFinalized<EventHandler> {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.h b/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.h
index d70251f..f0eb05f 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorDOMDebuggerAgent.h
@@ -44,7 +44,6 @@
 namespace blink {
 
 class Element;
-class Event;
 class InspectorDOMAgent;
 class Node;
 
diff --git a/third_party/WebKit/Source/core/inspector/InspectorHighlight.h b/third_party/WebKit/Source/core/inspector/InspectorHighlight.h
index 9ba347d..47702b0 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorHighlight.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorHighlight.h
@@ -16,10 +16,6 @@
 
 class Color;
 
-namespace protocol {
-class Value;
-}
-
 struct CORE_EXPORT InspectorHighlightConfig {
   USING_FAST_MALLOC(InspectorHighlightConfig);
 
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
index 3190bc29..ad6ad9aa 100644
--- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
+++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
@@ -42,7 +42,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ErrorEvent;
 class LocalFrame;
 class SecurityOrigin;
diff --git a/third_party/WebKit/Source/core/inspector/ThreadDebugger.h b/third_party/WebKit/Source/core/inspector/ThreadDebugger.h
index 811a1075e..cc687d13 100644
--- a/third_party/WebKit/Source/core/inspector/ThreadDebugger.h
+++ b/third_party/WebKit/Source/core/inspector/ThreadDebugger.h
@@ -18,7 +18,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ExecutionContext;
 class SourceLocation;
 
diff --git a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
index 3c0d42a..1313603 100644
--- a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
+++ b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
@@ -36,7 +36,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ErrorEvent;
 class SourceLocation;
 class WorkerThread;
diff --git a/third_party/WebKit/Source/core/layout/BidiRun.h b/third_party/WebKit/Source/core/layout/BidiRun.h
index e75d501..b119dd7 100644
--- a/third_party/WebKit/Source/core/layout/BidiRun.h
+++ b/third_party/WebKit/Source/core/layout/BidiRun.h
@@ -29,7 +29,6 @@
 
 namespace blink {
 
-class BidiContext;
 class InlineBox;
 
 struct BidiRun : BidiCharacterRun {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
index 301f912..97ae0e7d 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.h
@@ -57,8 +57,6 @@
 enum LinePositionMode { PositionOnContainingLine, PositionOfInteriorLineBoxes };
 enum LineDirectionMode { HorizontalLine, VerticalLine };
 
-class InlineFlowBox;
-
 struct LayoutBoxModelObjectRareData {
   WTF_MAKE_NONCOPYABLE(LayoutBoxModelObjectRareData);
   USING_FAST_MALLOC(LayoutBoxModelObjectRareData);
diff --git a/third_party/WebKit/Source/core/layout/line/InlineBox.h b/third_party/WebKit/Source/core/layout/line/InlineBox.h
index 95b6c6a..6254dc91 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineBox.h
+++ b/third_party/WebKit/Source/core/layout/line/InlineBox.h
@@ -33,6 +33,7 @@
 
 class HitTestRequest;
 class HitTestResult;
+class InlineFlowBox;
 class LayoutObject;
 class RootInlineBox;
 
diff --git a/third_party/WebKit/Source/core/layout/line/LayoutTextInfo.h b/third_party/WebKit/Source/core/layout/line/LayoutTextInfo.h
index 132ae95..d8541bc5 100644
--- a/third_party/WebKit/Source/core/layout/line/LayoutTextInfo.h
+++ b/third_party/WebKit/Source/core/layout/line/LayoutTextInfo.h
@@ -30,7 +30,6 @@
 namespace blink {
 
 class Font;
-class LayoutText;
 
 struct LayoutTextInfo {
   STACK_ALLOCATED();
diff --git a/third_party/WebKit/Source/core/layout/line/LineBoxList.h b/third_party/WebKit/Source/core/layout/line/LineBoxList.h
index 6b7525a..788bb5cf 100644
--- a/third_party/WebKit/Source/core/layout/line/LineBoxList.h
+++ b/third_party/WebKit/Source/core/layout/line/LineBoxList.h
@@ -43,7 +43,6 @@
 class LayoutUnit;
 class LineLayoutBoxModel;
 class LineLayoutItem;
-struct PaintInfo;
 
 class LineBoxList {
   DISALLOW_NEW();
diff --git a/third_party/WebKit/Source/core/layout/line/RootInlineBox.h b/third_party/WebKit/Source/core/layout/line/RootInlineBox.h
index 07bfbee20..0db8a365a 100644
--- a/third_party/WebKit/Source/core/layout/line/RootInlineBox.h
+++ b/third_party/WebKit/Source/core/layout/line/RootInlineBox.h
@@ -35,7 +35,6 @@
 class LineLayoutBlockFlow;
 
 struct BidiStatus;
-struct GapRects;
 
 class RootInlineBox : public InlineFlowBox {
  public:
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
index 1f4ed3b..21bebc49 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_layout_algorithm.h
@@ -14,7 +14,6 @@
 
 class ComputedStyle;
 class NGConstraintSpace;
-class NGPhysicalFragment;
 class NGBreakToken;
 
 // A class for inline layout (e.g. a anonymous block with inline-level children
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
index 117b63b..8c653fb 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
@@ -18,15 +18,12 @@
 namespace blink {
 
 class ComputedStyle;
-class LayoutBox;
 class LayoutObject;
 class NGConstraintSpace;
 class NGFragmentBase;
 class NGLayoutAlgorithm;
 class NGLayoutInlineItem;
 class NGLayoutInlineItemsBuilder;
-class NGPhysicalFragment;
-struct MinAndMaxContentSizes;
 
 // Represents an inline node to be laid out.
 class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
index ba343da6..9c1e9b3 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
@@ -17,7 +17,6 @@
 class NGConstraintSpace;
 class NGFragmentBase;
 class NGPhysicalFragmentBase;
-class NGPhysicalFragment;
 
 enum NGLayoutStatus { kNotFinished, kChildAlgorithmRequired, kNewFragment };
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
index f0098ec..9fdc770 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.h
@@ -14,7 +14,6 @@
 
 class ComputedStyle;
 class NGConstraintSpace;
-class NGPhysicalFragment;
 class NGBreakToken;
 
 // A class for text layout. This takes a NGInlineNode which consists only
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngineBaseline.h b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngineBaseline.h
index 32b5d76df..d868cc83 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngineBaseline.h
+++ b/third_party/WebKit/Source/core/layout/svg/SVGTextLayoutEngineBaseline.h
@@ -30,7 +30,6 @@
 
 class Font;
 class ComputedStyle;
-class SVGComputedStyle;
 
 // Helper class used by SVGTextLayoutEngine to handle 'alignment-baseline' /
 // 'dominant-baseline' and 'baseline-shift'.
diff --git a/third_party/WebKit/Source/core/loader/FrameLoaderClient.h b/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
index b2ed8bc..56ad147d 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
+++ b/third_party/WebKit/Source/core/loader/FrameLoaderClient.h
@@ -69,7 +69,6 @@
 class ResourceError;
 class ResourceRequest;
 class ResourceResponse;
-class ScriptState;
 class SecurityOrigin;
 class SharedWorkerRepositoryClient;
 class SubstituteData;
diff --git a/third_party/WebKit/Source/core/loader/ImageLoader.h b/third_party/WebKit/Source/core/loader/ImageLoader.h
index 5f9d444b..f37edbc9 100644
--- a/third_party/WebKit/Source/core/loader/ImageLoader.h
+++ b/third_party/WebKit/Source/core/loader/ImageLoader.h
@@ -35,7 +35,6 @@
 namespace blink {
 
 class IncrementLoadEventDelayCount;
-class Document;
 class Element;
 class ImageLoader;
 class LayoutImageResource;
diff --git a/third_party/WebKit/Source/core/loader/ProgressTracker.h b/third_party/WebKit/Source/core/loader/ProgressTracker.h
index 8d8b2a2..46dd165 100644
--- a/third_party/WebKit/Source/core/loader/ProgressTracker.h
+++ b/third_party/WebKit/Source/core/loader/ProgressTracker.h
@@ -38,7 +38,6 @@
 namespace blink {
 
 class LocalFrame;
-class Resource;
 class ResourceResponse;
 struct ProgressItem;
 
diff --git a/third_party/WebKit/Source/core/page/ChromeClient.h b/third_party/WebKit/Source/core/page/ChromeClient.h
index 3ee0973..330e0ff 100644
--- a/third_party/WebKit/Source/core/page/ChromeClient.h
+++ b/third_party/WebKit/Source/core/page/ChromeClient.h
@@ -76,9 +76,9 @@
 struct CompositedSelection;
 struct DateTimeChooserParameters;
 struct FrameLoadRequest;
-struct GraphicsDeviceAdapter;
 struct ViewportDescription;
 struct WebPoint;
+struct WebScreenInfo;
 struct WindowFeatures;
 
 class CORE_EXPORT ChromeClient : public HostWindow {
diff --git a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
index f24f38a..5b30bff 100644
--- a/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
+++ b/third_party/WebKit/Source/core/page/ScopedPageSuspender.h
@@ -26,8 +26,6 @@
 
 namespace blink {
 
-class Page;
-
 class CORE_EXPORT ScopedPageSuspender final {
   WTF_MAKE_NONCOPYABLE(ScopedPageSuspender);
   USING_FAST_MALLOC(ScopedPageSuspender);
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
index b0054b46..9f461f6 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
@@ -14,7 +14,6 @@
 class Element;
 class PaintLayer;
 class PaintLayerScrollableArea;
-class ScrollableArea;
 
 // Manages the root scroller associated with a given document. The root
 // scroller causes browser controls movement, overscroll effects and prevents
diff --git a/third_party/WebKit/Source/core/paint/ObjectPainter.h b/third_party/WebKit/Source/core/paint/ObjectPainter.h
index e61af62..04af85c 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPainter.h
+++ b/third_party/WebKit/Source/core/paint/ObjectPainter.h
@@ -17,7 +17,6 @@
 class LayoutPoint;
 struct PaintInfo;
 class LayoutObject;
-class ComputedStyle;
 
 class ObjectPainter {
   STACK_ALLOCATED();
diff --git a/third_party/WebKit/Source/core/svg/SVGPathBlender.h b/third_party/WebKit/Source/core/svg/SVGPathBlender.h
index 96f27893..050f309 100644
--- a/third_party/WebKit/Source/core/svg/SVGPathBlender.h
+++ b/third_party/WebKit/Source/core/svg/SVGPathBlender.h
@@ -25,7 +25,6 @@
 
 namespace blink {
 
-struct PathSegmentData;
 class SVGPathConsumer;
 class SVGPathByteStreamSource;
 
diff --git a/third_party/WebKit/Source/core/svg/SVGViewElement.h b/third_party/WebKit/Source/core/svg/SVGViewElement.h
index 9e3f2b9..f427066 100644
--- a/third_party/WebKit/Source/core/svg/SVGViewElement.h
+++ b/third_party/WebKit/Source/core/svg/SVGViewElement.h
@@ -28,8 +28,6 @@
 
 namespace blink {
 
-class SVGStaticStringList;
-
 class SVGViewElement final : public SVGElement,
                              public SVGFitToViewBox,
                              public SVGZoomAndPan {
diff --git a/third_party/WebKit/Source/core/timing/PerformanceBase.h b/third_party/WebKit/Source/core/timing/PerformanceBase.h
index 739d5a59..a66931b3 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceBase.h
+++ b/third_party/WebKit/Source/core/timing/PerformanceBase.h
@@ -47,7 +47,6 @@
 
 namespace blink {
 
-class DOMWindow;
 class ExceptionState;
 class LocalFrame;
 class PerformanceObserver;
diff --git a/third_party/WebKit/Source/core/timing/PerformanceLongTaskTiming.h b/third_party/WebKit/Source/core/timing/PerformanceLongTaskTiming.h
index fdda50f21..2784365c 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceLongTaskTiming.h
+++ b/third_party/WebKit/Source/core/timing/PerformanceLongTaskTiming.h
@@ -12,8 +12,6 @@
 
 namespace blink {
 
-class DOMWindow;
-
 class PerformanceLongTaskTiming final : public PerformanceEntry {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
index bae8ae0..43d3f1a 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
@@ -42,7 +42,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ExecutionContext;
 class InProcessWorkerMessagingProxy;
 class WorkerGlobalScope;
diff --git a/third_party/WebKit/Source/core/workers/WorkerReportingProxy.h b/third_party/WebKit/Source/core/workers/WorkerReportingProxy.h
index 42cd9876..d1f3a82 100644
--- a/third_party/WebKit/Source/core/workers/WorkerReportingProxy.h
+++ b/third_party/WebKit/Source/core/workers/WorkerReportingProxy.h
@@ -39,7 +39,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ParentFrameTaskRunners;
 class SourceLocation;
 class WorkerOrWorkletGlobalScope;
diff --git a/third_party/WebKit/Source/core/xml/XPathParser.h b/third_party/WebKit/Source/core/xml/XPathParser.h
index a313315..6cbae73 100644
--- a/third_party/WebKit/Source/core/xml/XPathParser.h
+++ b/third_party/WebKit/Source/core/xml/XPathParser.h
@@ -42,7 +42,6 @@
 class Expression;
 class LocationPath;
 class Parser;
-class Predicate;
 
 struct Token {
   STACK_ALLOCATED();
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h
index 9f8c50c..45b7a1a 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -45,7 +45,6 @@
 namespace blink {
 
 class AXObject;
-class AXObjectCache;
 class AXObjectCacheImpl;
 class Element;
 class FrameView;
diff --git a/third_party/WebKit/Source/modules/background_sync/SyncManager.h b/third_party/WebKit/Source/modules/background_sync/SyncManager.h
index 38aca04..10977ec 100644
--- a/third_party/WebKit/Source/modules/background_sync/SyncManager.h
+++ b/third_party/WebKit/Source/modules/background_sync/SyncManager.h
@@ -12,7 +12,6 @@
 
 namespace blink {
 
-class ExecutionContext;
 class ScriptPromise;
 class ScriptPromiseResolver;
 class ScriptState;
diff --git a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.h b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.h
index 64f9b9f..abcd58e 100644
--- a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.h
+++ b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.h
@@ -12,7 +12,6 @@
 
 namespace blink {
 
-class Blob;
 class ExceptionState;
 class ExecutionContext;
 class KURL;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
index edbb5b8..17177a84 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothAttributeInstanceMap.h
@@ -15,7 +15,6 @@
 
 class BluetoothDevice;
 class ExecutionContext;
-class ScriptPromiseResolver;
 
 struct WebBluetoothRemoteGATTCharacteristicInit;
 struct WebBluetoothRemoteGATTService;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
index 9d085bc..1db9949 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
@@ -21,7 +21,6 @@
 class BluetoothRemoteGATTCharacteristic;
 class BluetoothRemoteGATTServer;
 class BluetoothRemoteGATTService;
-class ScriptPromise;
 class ScriptPromiseResolver;
 
 struct WebBluetoothRemoteGATTCharacteristicInit;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
index 1dcdad9..fd0664c 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
@@ -22,7 +22,6 @@
 class BluetoothCharacteristicProperties;
 class ExecutionContext;
 class ScriptPromise;
-class ScriptPromiseResolver;
 class ScriptState;
 
 // BluetoothRemoteGATTCharacteristic represents a GATT Characteristic, which is
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
index 22c6015..b74bbbcf 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
@@ -18,7 +18,6 @@
 namespace blink {
 
 class ScriptPromise;
-class ScriptPromiseResolver;
 class ScriptState;
 
 // Represents a GATT Service within a Bluetooth Peripheral, a collection of
diff --git a/third_party/WebKit/Source/modules/credentialmanager/PasswordCredential.h b/third_party/WebKit/Source/modules/credentialmanager/PasswordCredential.h
index f3bb136b6..0d1940b 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/PasswordCredential.h
+++ b/third_party/WebKit/Source/modules/credentialmanager/PasswordCredential.h
@@ -16,7 +16,6 @@
 
 namespace blink {
 
-class FormData;
 class HTMLFormElement;
 class PasswordCredentialData;
 class WebPasswordCredential;
diff --git a/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.h b/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.h
index 6204b0a3..b3714fd 100644
--- a/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.h
+++ b/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.h
@@ -43,8 +43,6 @@
 
 namespace blink {
 
-class Dictionary;
-
 struct AlgorithmError {
   STACK_ALLOCATED();
   WebCryptoErrorType errorType;
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h b/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h
index fa0411d..904633e 100644
--- a/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h
+++ b/third_party/WebKit/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h
@@ -21,7 +21,6 @@
 class ScriptPromise;
 class ScriptState;
 class WebContentDecryptionModule;
-class WebMediaPlayer;
 
 class MODULES_EXPORT HTMLMediaElementEncryptedMedia final
     : public GarbageCollectedFinalized<HTMLMediaElementEncryptedMedia>,
diff --git a/third_party/WebKit/Source/modules/fetch/Response.h b/third_party/WebKit/Source/modules/fetch/Response.h
index a2f293e..0fdd3c6 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.h
+++ b/third_party/WebKit/Source/modules/fetch/Response.h
@@ -18,7 +18,6 @@
 
 namespace blink {
 
-class Blob;
 class ExceptionState;
 class ResponseInit;
 class ScriptState;
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
index f9cde57..daabc8c 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.h
@@ -42,7 +42,6 @@
 class ExceptionState;
 class MediaTrackConstraints;
 class MediaStream;
-class MediaStreamTrackSourcesCallback;
 class MediaTrackSettings;
 
 class MODULES_EXPORT MediaStreamTrack : public EventTargetWithInlineData,
diff --git a/third_party/WebKit/Source/modules/nfc/NFC.h b/third_party/WebKit/Source/modules/nfc/NFC.h
index 2847d87..b8c0eb7 100644
--- a/third_party/WebKit/Source/modules/nfc/NFC.h
+++ b/third_party/WebKit/Source/modules/nfc/NFC.h
@@ -18,7 +18,6 @@
 namespace blink {
 
 class MessageCallback;
-class NFCError;
 class NFCPushOptions;
 using NFCPushMessage = StringOrArrayBufferOrNFCMessage;
 class NFCWatchOptions;
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
index 0a168b8..c89f3e5 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCDataChannel.h
@@ -42,7 +42,6 @@
 class DOMArrayBuffer;
 class DOMArrayBufferView;
 class ExceptionState;
-class RTCPeerConnection;
 class WebRTCDataChannelHandler;
 class WebRTCPeerConnectionHandler;
 struct WebRTCDataChannelInit;
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.h
index 418b75dd..08a93c05 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.h
@@ -51,7 +51,6 @@
 class ExecutionContext;
 class WebServiceWorker;
 class WebServiceWorkerProvider;
-class WebServiceWorkerRegistration;
 
 class MODULES_EXPORT ServiceWorkerContainer final
     : public EventTargetWithInlineData,
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
index 0dacfa5b..1566128 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
@@ -42,7 +42,6 @@
 namespace blink {
 
 class Dictionary;
-class Request;
 class ScriptPromise;
 class ScriptState;
 class ServiceWorkerClients;
diff --git a/third_party/WebKit/Source/modules/storage/DOMWindowStorageController.h b/third_party/WebKit/Source/modules/storage/DOMWindowStorageController.h
index aecaa1f..693af6d0 100644
--- a/third_party/WebKit/Source/modules/storage/DOMWindowStorageController.h
+++ b/third_party/WebKit/Source/modules/storage/DOMWindowStorageController.h
@@ -14,7 +14,6 @@
 namespace blink {
 
 class Document;
-class Event;
 
 class MODULES_EXPORT DOMWindowStorageController final
     : public GarbageCollected<DOMWindowStorageController>,
diff --git a/third_party/WebKit/Source/modules/vr/VREyeParameters.h b/third_party/WebKit/Source/modules/vr/VREyeParameters.h
index 6e34b5a..9a3a00c4 100644
--- a/third_party/WebKit/Source/modules/vr/VREyeParameters.h
+++ b/third_party/WebKit/Source/modules/vr/VREyeParameters.h
@@ -15,8 +15,6 @@
 
 namespace blink {
 
-struct WebVREyeParameters;
-
 class VREyeParameters final : public GarbageCollected<VREyeParameters>,
                               public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContext.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContext.h
index de862377..9633bc455 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContext.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContext.h
@@ -37,12 +37,16 @@
 class EXTBlendMinMax;
 class EXTFragDepth;
 class EXTShaderTextureLOD;
+class EXTsRGB;
 class EXTTextureFilterAnisotropic;
 class OESElementIndexUint;
 class OESStandardDerivatives;
+class OESTextureFloat;
 class OESTextureFloatLinear;
+class OESTextureHalfFloat;
 class OESTextureHalfFloatLinear;
 class WebGLDebugRendererInfo;
+class WebGLDepthTexture;
 class WebGLLoseContext;
 
 class WebGLRenderingContext final : public WebGLRenderingContextBase {
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 0fce6f2..6f392dc3 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -68,7 +68,6 @@
 
 class EXTDisjointTimerQuery;
 class EXTDisjointTimerQueryWebGL2;
-class EXTsRGB;
 class ExceptionState;
 class HTMLCanvasElementOrOffscreenCanvas;
 class HTMLImageElement;
@@ -77,8 +76,6 @@
 class ImageBuffer;
 class ImageData;
 class IntSize;
-class OESTextureFloat;
-class OESTextureHalfFloat;
 class OESVertexArrayObject;
 class WebGLActiveInfo;
 class WebGLBuffer;
@@ -92,7 +89,6 @@
 class WebGLContextGroup;
 class WebGLContextObject;
 class WebGLDebugShaders;
-class WebGLDepthTexture;
 class WebGLDrawBuffers;
 class WebGLExtension;
 class WebGLFramebuffer;
diff --git a/third_party/WebKit/Source/platform/HostWindow.h b/third_party/WebKit/Source/platform/HostWindow.h
index 02afada..2ba8cf5 100644
--- a/third_party/WebKit/Source/platform/HostWindow.h
+++ b/third_party/WebKit/Source/platform/HostWindow.h
@@ -34,7 +34,6 @@
 namespace blink {
 class IntRect;
 class Widget;
-struct WebScreenInfo;
 
 class PLATFORM_EXPORT HostWindow
     : public GarbageCollectedFinalized<HostWindow> {
diff --git a/third_party/WebKit/Source/platform/animation/CompositorAnimationHost.h b/third_party/WebKit/Source/platform/animation/CompositorAnimationHost.h
index e1675c6..5b372a2b 100644
--- a/third_party/WebKit/Source/platform/animation/CompositorAnimationHost.h
+++ b/third_party/WebKit/Source/platform/animation/CompositorAnimationHost.h
@@ -13,10 +13,6 @@
 
 #include <memory>
 
-namespace cc {
-struct ScrollOffsetAnimationUpdate;
-}
-
 namespace blink {
 
 // A compositor representation for cc::AnimationHost.
diff --git a/third_party/WebKit/Source/platform/fonts/Font.h b/third_party/WebKit/Source/platform/fonts/Font.h
index 6930c04..b85ee0e 100644
--- a/third_party/WebKit/Source/platform/fonts/Font.h
+++ b/third_party/WebKit/Source/platform/fonts/Font.h
@@ -41,7 +41,6 @@
 
 class SkCanvas;
 class SkPaint;
-struct SkPoint;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h
index fa69d9b5..80910ae 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.h
@@ -45,7 +45,6 @@
 class Font;
 class SimpleFontData;
 class HarfBuzzShaper;
-class UnicodeRangeSet;
 
 // Shaping text runs is split into several stages: Run segmentation, shaping the
 // initial segment, identify shaped and non-shaped sequences of the shaping
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h
index f5f23e9..a1a5c42 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeCache.h
@@ -38,8 +38,6 @@
 
 namespace blink {
 
-class Font;
-
 struct ShapeCacheEntry {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
   ShapeCacheEntry() { m_shapeResult = nullptr; }
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
index d54042b..517bca8 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.h
@@ -48,7 +48,6 @@
 class ShapeResultSpacing;
 class SimpleFontData;
 class TextRun;
-struct GlyphData;
 
 class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
  public:
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
index 68021521..b48e4553 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
@@ -14,6 +14,7 @@
 
 struct CharacterRange;
 class GlyphBuffer;
+struct GlyphData;
 class TextRun;
 
 class ShapeResultBuffer {
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
index b34ee09e..cfc2ea3 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultInlineHeaders.h
@@ -40,7 +40,6 @@
 
 namespace blink {
 
-class Font;
 class SimpleFontData;
 
 struct HarfBuzzRunGlyphData {
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
index 0ccbe3a7..6f42d583 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
@@ -11,7 +11,6 @@
 namespace blink {
 
 class FontDescription;
-class ShapeResult;
 class TextRun;
 
 class PLATFORM_EXPORT ShapeResultSpacing final {
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
index 090b61f..7aa52e1b 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsContext.h
@@ -45,12 +45,10 @@
 #include <memory>
 
 class SkBitmap;
-class SkImage;
 class SkPaint;
 class SkPath;
 class SkPicture;
 class SkRRect;
-struct SkImageInfo;
 struct SkRect;
 
 namespace blink {
diff --git a/third_party/WebKit/Source/platform/graphics/OffscreenCanvasPlaceholder.h b/third_party/WebKit/Source/platform/graphics/OffscreenCanvasPlaceholder.h
index 73d4b9f4..f03040e 100644
--- a/third_party/WebKit/Source/platform/graphics/OffscreenCanvasPlaceholder.h
+++ b/third_party/WebKit/Source/platform/graphics/OffscreenCanvasPlaceholder.h
@@ -13,7 +13,6 @@
 namespace blink {
 
 class Image;
-class IntSize;
 class OffscreenCanvasFrameDispatcher;
 class WebTaskRunner;
 
diff --git a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
index 47857b74..7d32232 100644
--- a/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
+++ b/third_party/WebKit/Source/platform/graphics/PlaceholderImage.h
@@ -18,10 +18,7 @@
 
 namespace blink {
 
-class FloatPoint;
 class FloatRect;
-class FloatSize;
-class GraphicsContext;
 class ImageObserver;
 
 // A generated placeholder image that shows a translucent gray rectangle.
diff --git a/third_party/WebKit/Source/platform/heap/GCInfo.h b/third_party/WebKit/Source/platform/heap/GCInfo.h
index 8440ada..8d4d50d 100644
--- a/third_party/WebKit/Source/platform/heap/GCInfo.h
+++ b/third_party/WebKit/Source/platform/heap/GCInfo.h
@@ -135,10 +135,6 @@
   }
 };
 
-// s_gcInfoTable holds the per-class GCInfo descriptors; each heap
-// object header keeps its index into this table.
-extern PLATFORM_EXPORT GCInfo const** s_gcInfoTable;
-
 // GCInfo contains meta-data associated with objects allocated in the
 // Blink heap. This meta-data consists of a function pointer used to
 // trace the pointers in the object during garbage collection, an
@@ -156,6 +152,10 @@
   bool m_hasVTable;
 };
 
+// s_gcInfoTable holds the per-class GCInfo descriptors; each heap
+// object header keeps its index into this table.
+extern PLATFORM_EXPORT GCInfo const** s_gcInfoTable;
+
 #if ENABLE(ASSERT)
 PLATFORM_EXPORT void assertObjectHasGCInfo(const void*, size_t gcInfoIndex);
 #endif
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.h b/third_party/WebKit/Source/platform/heap/ThreadState.h
index 87339b1..6ab0903e 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.h
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.h
@@ -55,7 +55,6 @@
 
 class BasePage;
 class CallbackStack;
-struct GCInfo;
 class GarbageCollectedMixinConstructorMarker;
 class PersistentNode;
 class PersistentRegion;
diff --git a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
index b7d2736..5a0b92d 100644
--- a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
+++ b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
@@ -10,7 +10,6 @@
 
 namespace v8 {
 class Value;
-class Object;
 template <class T>
 class PersistentBase;
 }
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMac.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMac.h
index c12c27f..2dd3765 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMac.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMac.h
@@ -31,8 +31,6 @@
 
 typedef id ScrollbarPainter;
 
-class SkCanvas;
-
 namespace blink {
 
 class Pattern;
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index 18f2c50..417010a 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -349,6 +349,7 @@
     "tests/LinkSelectionTest.cpp",
     "tests/ListenerLeakTest.cpp",
     "tests/MHTMLTest.cpp",
+    "tests/MediaElementFillingViewportTest.cpp",
     "tests/NGInlineLayoutTest.cpp",
     "tests/PrerenderingTest.cpp",
     "tests/ProgrammaticScrollTest.cpp",
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
index 4b26611..cdbc3ba 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.h
@@ -42,7 +42,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class Document;
 class FetchEvent;
 class ServiceWorkerGlobalScope;
diff --git a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.h b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.h
index 0f786b3b..535b15ce 100644
--- a/third_party/WebKit/Source/web/WebDevToolsAgentImpl.h
+++ b/third_party/WebKit/Source/web/WebDevToolsAgentImpl.h
@@ -51,7 +51,6 @@
 class InspectorResourceContainer;
 class InspectorResourceContentLoader;
 class LocalFrame;
-class Page;
 class WebDevToolsAgentClient;
 class WebFrameWidgetImpl;
 class WebLayerTreeView;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetBase.h b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
index cea53c53..12d3a48 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetBase.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetBase.h
@@ -14,11 +14,9 @@
 
 class CompositorAnimationTimeline;
 class CompositorProxyClient;
-class DragController;
 class GraphicsLayer;
 class WebImage;
 class WebLayer;
-class WebLocalFrameImpl;
 class WebViewImpl;
 class HitTestResult;
 struct WebPoint;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
index 68889493..38148a1 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.h
@@ -51,7 +51,6 @@
 class Element;
 class InspectorOverlay;
 class LocalFrame;
-class Page;
 class PaintLayerCompositor;
 class UserGestureToken;
 class CompositorAnimationTimeline;
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.h b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
index 082e059..2d4b1d59 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
@@ -51,7 +51,6 @@
 class ChromePrintContext;
 class IntSize;
 class KURL;
-class Range;
 class ScrollableArea;
 class SharedWorkerRepositoryClientImpl;
 class TextFinder;
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.h b/third_party/WebKit/Source/web/WebPluginContainerImpl.h
index 6a73e03..631ab11 100644
--- a/third_party/WebKit/Source/web/WebPluginContainerImpl.h
+++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.h
@@ -42,8 +42,6 @@
 #include "wtf/Vector.h"
 #include "wtf/text/WTFString.h"
 
-struct NPObject;
-
 namespace blink {
 
 class GestureEvent;
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
index eed9546..fff32a62 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
@@ -47,7 +47,6 @@
 
 namespace blink {
 
-class ConsoleMessage;
 class ParentFrameTaskRunners;
 class WebApplicationCacheHost;
 class WebApplicationCacheHostClient;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index 9590b63..c03f41a 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -72,7 +72,6 @@
 namespace blink {
 
 class BrowserControls;
-class DataObject;
 class DevToolsEmulator;
 class Frame;
 class FullscreenController;
@@ -89,7 +88,6 @@
 class WebLayerTreeView;
 class WebLocalFrame;
 class WebLocalFrameImpl;
-class WebImage;
 class CompositorMutatorImpl;
 class WebPagePopupImpl;
 class WebPlugin;
diff --git a/third_party/WebKit/Source/web/tests/FakeWebPlugin.h b/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
index 667cc43e..aec93545 100644
--- a/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
+++ b/third_party/WebKit/Source/web/tests/FakeWebPlugin.h
@@ -39,7 +39,6 @@
 class WebFrame;
 class WebInputEvent;
 class WebPluginContainer;
-class WebURL;
 class WebURLResponse;
 struct WebPluginParams;
 
diff --git a/third_party/WebKit/Source/web/tests/MediaElementFillingViewportTest.cpp b/third_party/WebKit/Source/web/tests/MediaElementFillingViewportTest.cpp
new file mode 100644
index 0000000..5378c0b
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/MediaElementFillingViewportTest.cpp
@@ -0,0 +1,153 @@
+// Copyright 2016 The Chromium Authors. 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/Document.h"
+#include "core/html/HTMLMediaElement.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "web/tests/sim/SimCompositor.h"
+#include "web/tests/sim/SimDisplayItemList.h"
+#include "web/tests/sim/SimRequest.h"
+#include "web/tests/sim/SimTest.h"
+
+namespace blink {
+
+class MediaElementFillingViewportTest : public SimTest {
+ protected:
+  MediaElementFillingViewportTest() { webView().resize(WebSize(640, 480)); }
+
+  bool isMostlyFillingViewport(HTMLMediaElement* element) {
+    return element->m_mostlyFillingViewport;
+  }
+
+  bool viewportFillDebouncerTimerActive(HTMLMediaElement* element) {
+    return element->m_viewportFillDebouncerTimer.isActive();
+  }
+
+  void checkViewportIntersectionChanged(HTMLMediaElement* element) {
+    element->checkViewportIntersectionChanged();
+  }
+
+  std::unique_ptr<SimRequest> createMainResource() {
+    std::unique_ptr<SimRequest> mainResource =
+        wrapUnique(new SimRequest("https://example.com/", "text/html"));
+    loadURL("https://example.com");
+    return mainResource;
+  }
+};
+
+TEST_F(MediaElementFillingViewportTest, MostlyFillingViewport) {
+  std::unique_ptr<SimRequest> mainResource = createMainResource();
+  mainResource->complete(
+      "<!DOCTYPE html>"
+      "<html>"
+      "<video id='video' style = 'position:fixed; left:0; top:0; width:100%; "
+      "height:100%;'>"
+      "source src='test.webm'"
+      "</video>"
+      "</html>");
+  compositor().beginFrame();
+
+  HTMLMediaElement* element =
+      toElement<HTMLMediaElement>(document().getElementById("video"));
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_TRUE(viewportFillDebouncerTimerActive(element));
+  // TODO(xjz): Mock the time and check isMostlyFillingViewport() after 5s.
+}
+
+TEST_F(MediaElementFillingViewportTest, NotMostlyFillingViewport) {
+  std::unique_ptr<SimRequest> mainResource = createMainResource();
+  mainResource->complete(
+      "<!DOCTYPE html>"
+      "<html>"
+      "<video id='video' style = 'position:fixed; left:0; top:0; width:80%; "
+      "height:80%;'>"
+      "source src='test.webm'"
+      "</video>"
+      "</html>");
+  compositor().beginFrame();
+
+  HTMLMediaElement* element =
+      toElement<HTMLMediaElement>(document().getElementById("video"));
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_FALSE(viewportFillDebouncerTimerActive(element));
+}
+
+TEST_F(MediaElementFillingViewportTest, FillingViewportChanged) {
+  std::unique_ptr<SimRequest> mainResource = createMainResource();
+  mainResource->complete(
+      "<!DOCTYPE html>"
+      "<html>"
+      "<video id='video' style = 'position:fixed; left:0; top:0; width:100%; "
+      "height:100%;'>"
+      "source src='test.webm'"
+      "</video>"
+      "</html>");
+  compositor().beginFrame();
+
+  HTMLMediaElement* element =
+      toElement<HTMLMediaElement>(document().getElementById("video"));
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_TRUE(viewportFillDebouncerTimerActive(element));
+
+  element->setAttribute("style",
+                        "position:fixed; left:0; top:0; width:80%; height:80%;",
+                        ASSERT_NO_EXCEPTION);
+  compositor().beginFrame();
+
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_FALSE(viewportFillDebouncerTimerActive(element));
+}
+
+TEST_F(MediaElementFillingViewportTest, LargeVideo) {
+  std::unique_ptr<SimRequest> mainResource = createMainResource();
+  mainResource->complete(
+      "<!DOCTYPE html>"
+      "<html>"
+      "<video id='video' style = 'position:fixed; left:0; top:0; width:200%; "
+      "height:200%;'>"
+      "source src='test.webm'"
+      "</video>"
+      "</html>");
+  compositor().beginFrame();
+
+  HTMLMediaElement* element =
+      toElement<HTMLMediaElement>(document().getElementById("video"));
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_TRUE(viewportFillDebouncerTimerActive(element));
+}
+
+TEST_F(MediaElementFillingViewportTest, VideoScrollOutHalf) {
+  std::unique_ptr<SimRequest> mainResource = createMainResource();
+  mainResource->complete(
+      "<!DOCTYPE html>"
+      "<html>"
+      "<video id='video' style = 'position:fixed; left:0; top:0; width:100%; "
+      "height:100%;'>"
+      "source src='test.webm'"
+      "</video>"
+      "</html>");
+  compositor().beginFrame();
+
+  HTMLMediaElement* element =
+      toElement<HTMLMediaElement>(document().getElementById("video"));
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_TRUE(viewportFillDebouncerTimerActive(element));
+
+  element->setAttribute(
+      "style", "position:fixed; left:0; top:240px; width:100%; height:100%;",
+      ASSERT_NO_EXCEPTION);
+  compositor().beginFrame();
+  checkViewportIntersectionChanged(element);
+  EXPECT_FALSE(isMostlyFillingViewport(element));
+  EXPECT_FALSE(viewportFillDebouncerTimerActive(element));
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/wtf/HashTable.h b/third_party/WebKit/Source/wtf/HashTable.h
index 2afcfba7..8c398e58 100644
--- a/third_party/WebKit/Source/wtf/HashTable.h
+++ b/third_party/WebKit/Source/wtf/HashTable.h
@@ -86,6 +86,19 @@
 
 namespace WTF {
 
+// This is for tracing inside collections that have special support for weak
+// pointers. The trait has a trace method which returns true if there are weak
+// pointers to things that have not (yet) been marked live. Returning true
+// indicates that the entry in the collection may yet be removed by weak
+// handling. Default implementation for non-weak types is to use the regular
+// non-weak TraceTrait. Default implementation for types with weakness is to
+// call traceInCollection on the type's trait.
+template <WeakHandlingFlag weakHandlingFlag,
+          ShouldWeakPointersBeMarkedStrongly strongify,
+          typename T,
+          typename Traits>
+struct TraceInCollectionTrait;
+
 #if DUMP_HASHTABLE_STATS
 struct WTF_EXPORT HashTableStats {
   HashTableStats()
diff --git a/third_party/WebKit/Source/wtf/HashTraits.h b/third_party/WebKit/Source/wtf/HashTraits.h
index 061ae6a..6c9580a 100644
--- a/third_party/WebKit/Source/wtf/HashTraits.h
+++ b/third_party/WebKit/Source/wtf/HashTraits.h
@@ -419,19 +419,6 @@
   static T emptyValue() { return reinterpret_cast<T>(1); }
 };
 
-// This is for tracing inside collections that have special support for weak
-// pointers. The trait has a trace method which returns true if there are weak
-// pointers to things that have not (yet) been marked live. Returning true
-// indicates that the entry in the collection may yet be removed by weak
-// handling. Default implementation for non-weak types is to use the regular
-// non-weak TraceTrait. Default implementation for types with weakness is to
-// call traceInCollection on the type's trait.
-template <WeakHandlingFlag weakHandlingFlag,
-          ShouldWeakPointersBeMarkedStrongly strongify,
-          typename T,
-          typename Traits>
-struct TraceInCollectionTrait;
-
 }  // namespace WTF
 
 using WTF::HashTraits;
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index e8e786f..4264a4cd 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -57,8 +57,6 @@
 #include "cc/resources/shared_bitmap.h"
 #include "cc/surfaces/frame_sink_id.h"
 
-class GrContext;
-
 namespace gpu {
 class GpuMemoryBufferManager;
 }
@@ -89,7 +87,6 @@
 class WebGraphicsContext3DProvider;
 class WebIDBFactory;
 class WebImageCaptureFrameGrabber;
-class WebInstalledApp;
 class WebMIDIAccessor;
 class WebMIDIAccessorClient;
 class WebMediaPlayer;
@@ -102,7 +99,6 @@
 class WebNotificationManager;
 class WebPluginListBuilder;
 class WebPrescientNetworking;
-class WebProcessMemoryDump;
 class WebPublicSuffixList;
 class WebPushProvider;
 class WebRTCCertificateGenerator;
diff --git a/third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h b/third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h
index 3f170c6..4b97aeca 100644
--- a/third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h
+++ b/third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h
@@ -41,7 +41,6 @@
 
 class WebEncryptedMediaKeyInformation;
 class WebString;
-class WebURL;
 
 class BLINK_PLATFORM_EXPORT WebContentDecryptionModuleSession {
  public:
diff --git a/third_party/WebKit/public/platform/WebContentLayerClient.h b/third_party/WebKit/public/platform/WebContentLayerClient.h
index bbc2717..87f2d9f 100644
--- a/third_party/WebKit/public/platform/WebContentLayerClient.h
+++ b/third_party/WebKit/public/platform/WebContentLayerClient.h
@@ -34,7 +34,6 @@
 
 namespace blink {
 
-struct WebRect;
 class WebDisplayItemList;
 
 class BLINK_PLATFORM_EXPORT WebContentLayerClient {
diff --git a/third_party/WebKit/public/platform/WebCookieJar.h b/third_party/WebKit/public/platform/WebCookieJar.h
index 8b1ec34..46245899 100644
--- a/third_party/WebKit/public/platform/WebCookieJar.h
+++ b/third_party/WebKit/public/platform/WebCookieJar.h
@@ -36,9 +36,6 @@
 namespace blink {
 
 class WebURL;
-struct WebCookie;
-template <typename T>
-class WebVector;
 
 class WebCookieJar {
  public:
diff --git a/third_party/WebKit/public/platform/WebDisplayItemList.h b/third_party/WebKit/public/platform/WebDisplayItemList.h
index 6df9798..7bce0e8 100644
--- a/third_party/WebKit/public/platform/WebDisplayItemList.h
+++ b/third_party/WebKit/public/platform/WebDisplayItemList.h
@@ -17,7 +17,6 @@
 #include "third_party/skia/include/core/SkRegion.h"
 
 class SkColorFilter;
-class SkImageFilter;
 class SkMatrix44;
 class SkPicture;
 struct SkRect;
diff --git a/third_party/WebKit/public/platform/WebExternalBitmap.h b/third_party/WebKit/public/platform/WebExternalBitmap.h
index a71a268..2991fafd 100644
--- a/third_party/WebKit/public/platform/WebExternalBitmap.h
+++ b/third_party/WebKit/public/platform/WebExternalBitmap.h
@@ -33,8 +33,6 @@
 
 #include "WebSize.h"
 
-class SkColorSpace;
-
 namespace blink {
 
 class WebExternalBitmap {
diff --git a/third_party/WebKit/public/platform/WebFileWriter.h b/third_party/WebKit/public/platform/WebFileWriter.h
index 0e6dfff..949c511 100644
--- a/third_party/WebKit/public/platform/WebFileWriter.h
+++ b/third_party/WebKit/public/platform/WebFileWriter.h
@@ -37,7 +37,6 @@
 namespace blink {
 
 class WebString;
-class WebURL;
 
 class WebFileWriter {
  public:
diff --git a/third_party/WebKit/public/platform/WebFrameScheduler.h b/third_party/WebKit/public/platform/WebFrameScheduler.h
index fe6f3a67..ec075a2 100644
--- a/third_party/WebKit/public/platform/WebFrameScheduler.h
+++ b/third_party/WebKit/public/platform/WebFrameScheduler.h
@@ -11,7 +11,6 @@
 
 namespace blink {
 
-class WebSecurityOrigin;
 class WebTaskRunner;
 class WebViewScheduler;
 
diff --git a/third_party/WebKit/public/platform/WebImageLayer.h b/third_party/WebKit/public/platform/WebImageLayer.h
index 6159124..2562e28c 100644
--- a/third_party/WebKit/public/platform/WebImageLayer.h
+++ b/third_party/WebKit/public/platform/WebImageLayer.h
@@ -29,7 +29,6 @@
 #include "WebCommon.h"
 #include "WebLayer.h"
 
-class SkBitmap;
 class SkImage;
 
 namespace blink {
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index f9408b6..73e51c4 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -39,7 +39,6 @@
 #include "WebVector.h"
 
 class SkMatrix44;
-class SkImageFilter;
 
 namespace cc {
 class Layer;
diff --git a/third_party/WebKit/public/platform/WebLayerTreeView.h b/third_party/WebKit/public/platform/WebLayerTreeView.h
index f437e186..6550def 100644
--- a/third_party/WebKit/public/platform/WebLayerTreeView.h
+++ b/third_party/WebKit/public/platform/WebLayerTreeView.h
@@ -34,8 +34,6 @@
 #include "WebFloatPoint.h"
 #include "WebSize.h"
 
-class SkBitmap;
-
 namespace cc {
 class AnimationTimeline;
 }
@@ -46,9 +44,7 @@
 class WebLayer;
 class WebLayoutAndPaintAsyncCallback;
 struct WebPoint;
-struct WebSelectionBound;
 class WebSelection;
-class WebWidget;
 
 class WebLayerTreeView {
  public:
diff --git a/third_party/WebKit/public/platform/WebMediaPlayer.h b/third_party/WebKit/public/platform/WebMediaPlayer.h
index 7923285..f82ba33 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayer.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayer.h
@@ -229,6 +229,10 @@
   virtual void enteredFullscreen() {}
   virtual void exitedFullscreen() {}
 
+  // Inform WebMediaPlayer when the element starts/stops being the dominant
+  // visible content.
+  virtual void becameDominantVisibleContent(bool isDominant) {}
+
   virtual void enabledAudioTracksChanged(
       const WebVector<TrackId>& enabledTrackIds) {}
   // |selectedTrackId| is null if no track is selected.
diff --git a/third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h b/third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h
index 08b2f89..71f9289 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h
@@ -36,9 +36,6 @@
 
 namespace blink {
 
-class WebString;
-class WebURL;
-
 class BLINK_PLATFORM_EXPORT WebMediaPlayerEncryptedMediaClient {
  public:
   virtual void encrypted(WebEncryptedMediaInitDataType,
diff --git a/third_party/WebKit/public/platform/WebRTCStatsRequest.h b/third_party/WebKit/public/platform/WebRTCStatsRequest.h
index 91ed792d..22d20667 100644
--- a/third_party/WebKit/public/platform/WebRTCStatsRequest.h
+++ b/third_party/WebKit/public/platform/WebRTCStatsRequest.h
@@ -39,7 +39,6 @@
 
 class RTCStatsRequest;
 class WebMediaStreamTrack;
-class WebMediaStream;
 class WebRTCStatsResponse;
 
 // The WebRTCStatsRequest class represents a JavaScript call on
diff --git a/third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabase.h b/third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabase.h
index dd91ff66..1b08e08 100644
--- a/third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabase.h
+++ b/third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabase.h
@@ -38,7 +38,6 @@
 
 class WebData;
 class WebIDBCallbacks;
-class WebIDBDatabaseCallbacks;
 class WebIDBKey;
 class WebIDBKeyPath;
 class WebIDBKeyRange;
diff --git a/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h b/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
index 8f70597..9782873 100644
--- a/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
+++ b/third_party/WebKit/public/platform/modules/push_messaging/WebPushClient.h
@@ -13,7 +13,6 @@
 namespace blink {
 
 class WebServiceWorkerRegistration;
-struct WebPushSubscription;
 struct WebPushSubscriptionOptions;
 
 class WebPushClient {
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProvider.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProvider.h
index 08885fa..513cb9d 100644
--- a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProvider.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProvider.h
@@ -40,7 +40,6 @@
 namespace blink {
 
 class WebURL;
-class WebServiceWorker;
 class WebServiceWorkerProviderClient;
 struct WebServiceWorkerError;
 
diff --git a/third_party/WebKit/public/platform/scheduler/base/task_queue.h b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
index 877a8d2..07335fb0 100644
--- a/third_party/WebKit/public/platform/scheduler/base/task_queue.h
+++ b/third_party/WebKit/public/platform/scheduler/base/task_queue.h
@@ -20,11 +20,7 @@
 
 namespace blink {
 namespace scheduler {
-namespace internal {
-class TaskQueueImpl;
-}  // namespace internal
-class FakeWebTaskRunner;
-class LazyNow;
+
 class TimeDomain;
 
 class BLINK_PLATFORM_EXPORT TaskQueue : public base::SingleThreadTaskRunner {
diff --git a/third_party/WebKit/public/platform/scheduler/child/worker_scheduler.h b/third_party/WebKit/public/platform/scheduler/child/worker_scheduler.h
index 4dd78e2c8..1d814b1 100644
--- a/third_party/WebKit/public/platform/scheduler/child/worker_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/child/worker_scheduler.h
@@ -13,10 +13,6 @@
 #include "public/platform/scheduler/child/single_thread_idle_task_runner.h"
 #include "public/platform/WebCommon.h"
 
-namespace base {
-class MessageLoop;
-}
-
 namespace blink {
 namespace scheduler {
 class SchedulerTqmDelegate;
diff --git a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
index aef2cf3..b4cd79b 100644
--- a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
@@ -28,7 +28,6 @@
 }
 
 namespace blink {
-class WebLocalFrame;
 class WebThread;
 }
 
diff --git a/third_party/WebKit/public/web/WebAXObject.h b/third_party/WebKit/public/web/WebAXObject.h
index d15de28..3666068d 100644
--- a/third_party/WebKit/public/web/WebAXObject.h
+++ b/third_party/WebKit/public/web/WebAXObject.h
@@ -39,13 +39,6 @@
 #include "WebAXEnums.h"
 #include <memory>
 
-#if BLINK_IMPLEMENTATION
-namespace WTF {
-template <typename T>
-class PassRefPtr;
-}
-#endif
-
 class SkMatrix44;
 
 namespace blink {
diff --git a/third_party/WebKit/public/web/WebAssociatedURLLoader.h b/third_party/WebKit/public/web/WebAssociatedURLLoader.h
index 388ddf8..e2a9073 100644
--- a/third_party/WebKit/public/web/WebAssociatedURLLoader.h
+++ b/third_party/WebKit/public/web/WebAssociatedURLLoader.h
@@ -36,7 +36,6 @@
 namespace blink {
 
 class WebAssociatedURLLoaderClient;
-class WebLocalFrameImpl;
 class WebTaskRunner;
 class WebURLRequest;
 
diff --git a/third_party/WebKit/public/web/WebAutofillClient.h b/third_party/WebKit/public/web/WebAutofillClient.h
index 86a5a5e..072f7eb 100644
--- a/third_party/WebKit/public/web/WebAutofillClient.h
+++ b/third_party/WebKit/public/web/WebAutofillClient.h
@@ -34,13 +34,8 @@
 namespace blink {
 
 class WebFormControlElement;
-class WebFormElement;
 class WebInputElement;
 class WebKeyboardEvent;
-class WebNode;
-
-template <typename T>
-class WebVector;
 
 class WebAutofillClient {
  public:
diff --git a/third_party/WebKit/public/web/WebDevToolsAgentClient.h b/third_party/WebKit/public/web/WebDevToolsAgentClient.h
index e00795b..dad2f46 100644
--- a/third_party/WebKit/public/web/WebDevToolsAgentClient.h
+++ b/third_party/WebKit/public/web/WebDevToolsAgentClient.h
@@ -38,7 +38,6 @@
 
 class WebLocalFrame;
 class WebString;
-struct WebDeviceEmulationParams;
 
 class WebDevToolsAgentClient {
  public:
diff --git a/third_party/WebKit/public/web/WebDevToolsFrontend.h b/third_party/WebKit/public/web/WebDevToolsFrontend.h
index 5467668..161c2bf 100644
--- a/third_party/WebKit/public/web/WebDevToolsFrontend.h
+++ b/third_party/WebKit/public/web/WebDevToolsFrontend.h
@@ -37,8 +37,6 @@
 
 class WebDevToolsFrontendClient;
 class WebLocalFrame;
-class WebString;
-class WebView;
 
 // WebDevToolsFrontend represents DevTools client sitting in the Glue. It
 // provides direct and delegate Apis to the host.
diff --git a/third_party/WebKit/public/web/WebDocument.h b/third_party/WebKit/public/web/WebDocument.h
index a8ead29..b126371 100644
--- a/third_party/WebKit/public/web/WebDocument.h
+++ b/third_party/WebKit/public/web/WebDocument.h
@@ -40,13 +40,6 @@
 #include "public/platform/WebSecurityOrigin.h"
 #include "public/platform/WebVector.h"
 
-#if BLINK_IMPLEMENTATION
-namespace WTF {
-template <typename T>
-class PassRefPtr;
-}
-#endif
-
 namespace v8 {
 class Value;
 template <class T>
@@ -56,7 +49,6 @@
 namespace blink {
 
 class Document;
-class DocumentType;
 class WebAXObject;
 class WebElement;
 class WebFormElement;
diff --git a/third_party/WebKit/public/web/WebElementCollection.h b/third_party/WebKit/public/web/WebElementCollection.h
index 23e2f7c..a446100 100644
--- a/third_party/WebKit/public/web/WebElementCollection.h
+++ b/third_party/WebKit/public/web/WebElementCollection.h
@@ -37,10 +37,6 @@
 
 #if BLINK_IMPLEMENTATION
 #include "platform/heap/Handle.h"
-namespace WTF {
-template <typename T>
-class PassRefPtr;
-}
 #endif
 
 namespace blink {
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 0217b58..2dae4ef 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -39,12 +39,9 @@
 #include "public/web/WebTreeScopeType.h"
 #include <memory>
 
-struct NPObject;
-
 namespace v8 {
 class Context;
 class Function;
-class Object;
 class Value;
 template <class T>
 class Local;
@@ -63,7 +60,6 @@
 class WebDocument;
 class WebElement;
 class WebFrameImplBase;
-class WebLayer;
 class WebLocalFrame;
 class WebPerformance;
 class WebRemoteFrame;
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index f07b51c0..2593df06f 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -76,7 +76,6 @@
 class WebColorChooserClient;
 class WebContentDecryptionModule;
 class WebCookieJar;
-class WebCString;
 class WebDataSource;
 class WebEncryptedMediaClient;
 class WebExternalPopupMenu;
@@ -89,7 +88,6 @@
 class WebMediaPlayerEncryptedMediaClient;
 class WebMediaPlayerSource;
 class WebMediaSession;
-class WebMediaStream;
 class WebServiceWorkerProvider;
 class WebPlugin;
 class WebPresentationClient;
diff --git a/third_party/WebKit/public/web/WebFrameSerializerClient.h b/third_party/WebKit/public/web/WebFrameSerializerClient.h
index bb2cdbf4..752ece70 100644
--- a/third_party/WebKit/public/web/WebFrameSerializerClient.h
+++ b/third_party/WebKit/public/web/WebFrameSerializerClient.h
@@ -34,7 +34,6 @@
 namespace blink {
 
 class WebCString;
-class WebURL;
 
 // This class is used for providing sink interface that can be used to receive
 // the individual chunks of data to be saved.
diff --git a/third_party/WebKit/public/web/WebInputElement.h b/third_party/WebKit/public/web/WebInputElement.h
index 7872541..f78e0503 100644
--- a/third_party/WebKit/public/web/WebInputElement.h
+++ b/third_party/WebKit/public/web/WebInputElement.h
@@ -36,7 +36,6 @@
 namespace blink {
 
 class HTMLInputElement;
-class WebElementCollection;
 class WebOptionElement;
 
 // Provides readonly access to some properties of a DOM input element node.
diff --git a/third_party/WebKit/public/web/WebLabelElement.h b/third_party/WebKit/public/web/WebLabelElement.h
index be1b93d..24b97f8 100644
--- a/third_party/WebKit/public/web/WebLabelElement.h
+++ b/third_party/WebKit/public/web/WebLabelElement.h
@@ -33,13 +33,6 @@
 
 #include "WebElement.h"
 
-#if BLINK_IMPLEMENTATION
-namespace WTF {
-template <typename T>
-class PassRefPtr;
-}
-#endif
-
 namespace blink {
 
 class HTMLLabelElement;
diff --git a/third_party/WebKit/public/web/WebPluginContainer.h b/third_party/WebKit/public/web/WebPluginContainer.h
index ecacd8ac..51ddf9b 100644
--- a/third_party/WebKit/public/web/WebPluginContainer.h
+++ b/third_party/WebKit/public/web/WebPluginContainer.h
@@ -35,8 +35,6 @@
 #include "../platform/WebCommon.h"
 #include <v8.h>
 
-struct NPObject;
-
 namespace blink {
 
 class WebDocument;
diff --git a/third_party/WebKit/public/web/WebRange.h b/third_party/WebKit/public/web/WebRange.h
index 04af3e5..16732a9d 100644
--- a/third_party/WebKit/public/web/WebRange.h
+++ b/third_party/WebKit/public/web/WebRange.h
@@ -41,7 +41,6 @@
 class LocalFrame;
 class PlainTextRange;
 class Range;
-class WebString;
 
 class WebRange final {
  public:
diff --git a/third_party/WebKit/public/web/WebSharedWorkerClient.h b/third_party/WebKit/public/web/WebSharedWorkerClient.h
index b516f1f..14bc7c3 100644
--- a/third_party/WebKit/public/web/WebSharedWorkerClient.h
+++ b/third_party/WebKit/public/web/WebSharedWorkerClient.h
@@ -43,7 +43,6 @@
 class WebSecurityOrigin;
 class WebServiceWorkerNetworkProvider;
 class WebString;
-class WebWorker;
 class WebWorkerContentSettingsClientProxy;
 
 // Provides an interface back to the in-page script object for a worker.
diff --git a/third_party/WebKit/public/web/WebSpellCheckClient.h b/third_party/WebKit/public/web/WebSpellCheckClient.h
index 86d05fe..d0945d8 100644
--- a/third_party/WebKit/public/web/WebSpellCheckClient.h
+++ b/third_party/WebKit/public/web/WebSpellCheckClient.h
@@ -38,7 +38,6 @@
 
 class WebString;
 class WebTextCheckingCompletion;
-struct WebTextCheckingResult;
 
 class WebSpellCheckClient {
  public:
diff --git a/third_party/WebKit/public/web/WebView.h b/third_party/WebKit/public/web/WebView.h
index 3127234..eff1509 100644
--- a/third_party/WebKit/public/web/WebView.h
+++ b/third_party/WebKit/public/web/WebView.h
@@ -43,10 +43,8 @@
 namespace blink {
 
 class WebAXObject;
-class WebAutofillClient;
 class WebCompositedDisplayList;
 class WebCredentialManagerClient;
-class WebDragData;
 class WebFrame;
 class WebHitTestResult;
 class WebLocalFrame;
diff --git a/third_party/WebKit/public/web/WebViewClient.h b/third_party/WebKit/public/web/WebViewClient.h
index dd2c49a5..af3fe7c 100644
--- a/third_party/WebKit/public/web/WebViewClient.h
+++ b/third_party/WebKit/public/web/WebViewClient.h
@@ -41,7 +41,6 @@
 
 namespace blink {
 
-class WebAXObject;
 class WebDateTimeChooserCompletion;
 class WebFileChooserCompletion;
 class WebHitTestResult;
@@ -54,7 +53,6 @@
 class WebWidget;
 struct WebDateTimeChooserParams;
 struct WebPoint;
-struct WebPopupMenuInfo;
 struct WebRect;
 struct WebSize;
 struct WebWindowFeatures;
diff --git a/third_party/WebKit/public/web/WebWidget.h b/third_party/WebKit/public/web/WebWidget.h
index f5733a9..7641bb13 100644
--- a/third_party/WebKit/public/web/WebWidget.h
+++ b/third_party/WebKit/public/web/WebWidget.h
@@ -50,7 +50,6 @@
 class WebInputEvent;
 class WebLayoutAndPaintAsyncCallback;
 class WebPagePopup;
-class WebString;
 struct WebPoint;
 template <typename T>
 class WebVector;
diff --git a/third_party/WebKit/public/web/WebWidgetClient.h b/third_party/WebKit/public/web/WebWidgetClient.h
index 5ac5dff4..d5c11195 100644
--- a/third_party/WebKit/public/web/WebWidgetClient.h
+++ b/third_party/WebKit/public/web/WebWidgetClient.h
@@ -48,7 +48,6 @@
 class WebDragData;
 class WebGestureEvent;
 class WebImage;
-class WebLocalFrame;
 class WebNode;
 class WebString;
 class WebWidget;
@@ -56,7 +55,6 @@
 struct WebFloatPoint;
 struct WebFloatRect;
 struct WebFloatSize;
-struct WebSize;
 
 class WebWidgetClient {
  public:
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py
index e0a5767..f236444 100644
--- a/tools/perf/benchmarks/benchmark_smoke_unittest.py
+++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -53,9 +53,10 @@
       def CreateStorySet(self, options):
         # pylint: disable=super-on-old-class
         story_set = super(SinglePageBenchmark, self).CreateStorySet(options)
-        for story in story_set.stories:
-          story_set.stories = [story]
-          break
+        # Only smoke test the first story since smoke testing everything takes
+        # too long.
+        for s in story_set.stories[1:]:
+          story_set.RemoveStory(s)
         return story_set
 
     # Set the benchmark's default arguments.
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 74026c9..2f7d31f 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -70,8 +70,11 @@
       def CreateStorySet(self, options):
         # pylint: disable=super-on-old-class
         story_set = super(SinglePageBenchmark, self).CreateStorySet(options)
-        assert story_to_smoke_test in story_set.stories
-        story_set.stories = [story_to_smoke_test]
+        stories_to_remove = [s for s in story_set.stories if s !=
+                             story_to_smoke_test]
+        for s in stories_to_remove:
+          story_set.RemoveStory(s)
+        assert story_set.stories
         return story_set
 
     options = GenerateBenchmarkOptions(benchmark_class)