diff --git a/DEPS b/DEPS
index 5193581..d82411c 100644
--- a/DEPS
+++ b/DEPS
@@ -121,7 +121,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ede88ab2b4144bef441a928003cb2147abbdbb2c',
+  'skia_revision': 'eceb19f37e0ab68a77618239c33e163ea697bb04',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -133,7 +133,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'd51fbe347e70b1d3c338a215743fab079d1079e0',
+  'angle_revision': '4c94788c9c8533ae40aeb87f26d45fd7ca112f23',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -181,7 +181,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '514fe3e70db895bb83486d1349b7cfe8ff8b52d6',
+  'catapult_revision': '556d7714fda3de581bcc7b18e612a4aa63fdd244',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -245,7 +245,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'da8ee83ee72710b3c1885f63e489917a194089b1',
+  'dawn_revision': '1c0b0474c1a01a88e7e51a0b5a18771d96aacf88',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -679,7 +679,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'accf90556a957367425cacaad87ab762138b4e24',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd403a66dc72a6484c0a6e8693be9c44eea2c0d26',
       'condition': 'checkout_linux',
   },
 
@@ -704,7 +704,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'bdb1123726a2810c5f3e28f28f4802fa469e6914',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b19e8dff155d6bb50ddbb8b7ddd12b4b28fec7b4',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1036,7 +1036,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '0e3138ee22917db8e6e24cb809b9a384fe07662e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '038fb113a1f0c941c6b04ae63fc93acc8b3d0ec1',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1240,7 +1240,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@80ca3eda94ce278ca56f5c180aa87bbeadd95949',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6ca723bad79bad35876ae2fa540536f51b14084a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_draw_fn_impl.cc b/android_webview/browser/aw_draw_fn_impl.cc
index 168e2c8..1e232451 100644
--- a/android_webview/browser/aw_draw_fn_impl.cc
+++ b/android_webview/browser/aw_draw_fn_impl.cc
@@ -469,6 +469,24 @@
   // Ask GL to wait on any Vk sync_fd before writing.
   gpu::InsertEglFenceAndWait(std::move(pending_draw_->sync_fd));
 
+  // Calculate color space.
+  skcms_TransferFunction transfer_fn{
+      params->transfer_function_g, params->transfer_function_a,
+      params->transfer_function_b, params->transfer_function_c,
+      params->transfer_function_d, params->transfer_function_e,
+      params->transfer_function_f};
+  skcms_Matrix3x3 to_xyz;
+  static_assert(sizeof(to_xyz.vals) == sizeof(params->color_space_toXYZD50),
+                "Color space matrix sizes do not match");
+  memcpy(&to_xyz.vals[0][0], &params->color_space_toXYZD50[0],
+         sizeof(to_xyz.vals));
+  sk_sp<SkColorSpace> color_space = SkColorSpace::MakeRGB(transfer_fn, to_xyz);
+  if (!color_space) {
+    // If we weren't passed a valid colorspace, default to sRGB.
+    LOG(ERROR) << "Received invalid colorspace.";
+    color_space = SkColorSpace::MakeSRGB();
+  }
+
   // Bind buffer and render with GL.
   base::ScopedFD gl_done_fd;
   {
@@ -487,6 +505,7 @@
     hr_params.width = params->width;
     hr_params.height = params->height;
     hr_params.is_layer = params->is_layer;
+    hr_params.color_space = gfx::ColorSpace(*color_space);
 
     static_assert(base::size(decltype(params->transform){}) ==
                       base::size(hr_params.transform),
@@ -500,19 +519,8 @@
 
   // Create a GrVkSecondaryCBDrawContext to render our AHB w/ Vulkan.
   // TODO(ericrk): Handle non-RGBA.
-  skcms_TransferFunction transfer_fn{
-      params->transfer_function_g, params->transfer_function_a,
-      params->transfer_function_b, params->transfer_function_c,
-      params->transfer_function_d, params->transfer_function_e,
-      params->transfer_function_f};
-  skcms_Matrix3x3 to_xyz;
-  static_assert(sizeof(to_xyz.vals) == sizeof(params->color_space_toXYZD50),
-                "Color space matrix sizes do not match");
-  memcpy(&to_xyz.vals[0][0], &params->color_space_toXYZD50[0],
-         sizeof(to_xyz.vals));
-  sk_sp<SkColorSpace> color_space = SkColorSpace::MakeRGB(transfer_fn, to_xyz);
-  // TODO(ericrk): Use colorspace.
-  SkImageInfo info = SkImageInfo::MakeN32Premul(params->width, params->height);
+  SkImageInfo info =
+      SkImageInfo::MakeN32Premul(params->width, params->height, color_space);
   VkRect2D draw_bounds;
   GrVkDrawableInfo drawable_info{
       .fSecondaryCommandBuffer = params->secondary_command_buffer,
@@ -576,7 +584,7 @@
                                    pending_draw_->image_info);
   pending_draw_->ahb_skimage = SkImage::MakeFromTexture(
       vk_state_->gr_context(), backend_texture, kBottomLeft_GrSurfaceOrigin,
-      kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
+      kRGBA_8888_SkColorType, kPremul_SkAlphaType, color_space);
   if (!pending_draw_->ahb_skimage) {
     LOG(ERROR) << "Could not create SkImage from VkImage.";
     return;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7f43902b..7e2302fd 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1512,6 +1512,8 @@
     "shell/content/client/shell_content_browser_client.h",
     "shell/content/client/shell_main_delegate.cc",
     "shell/content/client/shell_main_delegate.h",
+    "shell/content/embedded_browser.cc",
+    "shell/content/embedded_browser.h",
   ]
 
   deps = [
@@ -1547,6 +1549,7 @@
     "//services/ws/ime/test_ime_driver:lib",
     "//services/ws/ime/test_ime_driver:manifest",
     "//services/ws/ime/test_ime_driver/public/mojom",
+    "//services/ws/remote_view_host",
     "//skia",
     "//ui/aura",
     "//ui/base",
@@ -1557,6 +1560,7 @@
     "//ui/message_center",
     "//ui/views:test_support",
     "//ui/views/examples:views_examples_with_content_lib",
+    "//ui/views/mus/remote_view:remote_view_provider",
     "//ui/wm",
   ]
 }
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 20785e6a..d92653c 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -803,10 +803,6 @@
       mojom::DictationToggleSource::kKeyboard);
 }
 
-bool CanHandleToggleDockedMagnifier() {
-  return features::IsDockedMagnifierEnabled();
-}
-
 bool CanHandleToggleOverview() {
   auto windows = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
   // Do not toggle overview if there is a window being dragged.
@@ -851,7 +847,6 @@
 }
 
 void HandleToggleDockedMagnifier() {
-  DCHECK(features::IsDockedMagnifierEnabled());
   base::RecordAction(UserMetricsAction("Accel_Toggle_Docked_Magnifier"));
 
   DockedMagnifierController* docked_magnifier_controller =
@@ -990,8 +985,7 @@
 
 bool CanHandleActiveMagnifierZoom() {
   return Shell::Get()->magnification_controller()->IsEnabled() ||
-         (features::IsDockedMagnifierEnabled() &&
-          Shell::Get()->docked_magnifier_controller()->GetEnabled());
+         Shell::Get()->docked_magnifier_controller()->GetEnabled();
 }
 
 // Change the scale of the active magnifier.
@@ -1001,8 +995,7 @@
     return;
   }
 
-  if (features::IsDockedMagnifierEnabled() &&
-      Shell::Get()->docked_magnifier_controller()->GetEnabled()) {
+  if (Shell::Get()->docked_magnifier_controller()->GetEnabled()) {
     Shell::Get()->docked_magnifier_controller()->StepToNextScaleValue(
         delta_index);
   }
@@ -1322,7 +1315,7 @@
     case TOGGLE_DICTATION:
       return CanHandleToggleDictation();
     case TOGGLE_DOCKED_MAGNIFIER:
-      return CanHandleToggleDockedMagnifier();
+      return true;
     case TOGGLE_FULLSCREEN_MAGNIFIER:
       return true;
     case TOGGLE_MESSAGE_CENTER_BUBBLE:
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 7a2ce32..5f85b081 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -17,7 +17,6 @@
 #include "ash/magnifier/docked_magnifier_controller.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/media/media_controller.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/interfaces/ime_info.mojom.h"
@@ -42,7 +41,6 @@
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/test/metrics/user_action_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "media/base/media_switches.h"
 #include "services/media_session/public/cpp/test/test_media_controller.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
@@ -1455,14 +1453,6 @@
   MagnifiersAcceleratorsTester() = default;
   ~MagnifiersAcceleratorsTester() override = default;
 
-  // AcceleratorControllerTest:
-  void SetUp() override {
-    // Explicitly enable the Docked Magnifier feature for the tests.
-    scoped_feature_list_.InitAndEnableFeature(features::kDockedMagnifier);
-
-    AcceleratorControllerTest::SetUp();
-  }
-
   DockedMagnifierController* docked_magnifier_controller() const {
     return Shell::Get()->docked_magnifier_controller();
   }
@@ -1472,8 +1462,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(MagnifiersAcceleratorsTester);
 };
 
diff --git a/ash/accelerators/ash_focus_manager_factory.cc b/ash/accelerators/ash_focus_manager_factory.cc
index 3c4af4d..18e9611 100644
--- a/ash/accelerators/ash_focus_manager_factory.cc
+++ b/ash/accelerators/ash_focus_manager_factory.cc
@@ -9,7 +9,6 @@
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/magnifier/docked_magnifier_controller.h"
 #include "ash/magnifier/magnification_controller.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/views/focus/focus_manager.h"
@@ -60,8 +59,7 @@
 
   gfx::Point point_of_interest = bounds_in_screen.CenterPoint();
   const ui::InputMethod* input_method = focused_now->GetInputMethod();
-  const bool docked_magnifier_enabled =
-      features::IsDockedMagnifierEnabled() && docked_magnifier->GetEnabled();
+  const bool docked_magnifier_enabled = docked_magnifier->GetEnabled();
   if (input_method && input_method->GetTextInputClient() &&
       input_method->GetTextInputClient()->GetTextInputType() !=
           ui::TEXT_INPUT_TYPE_NONE) {
diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc
index 061a8d9..bbc4f8b 100644
--- a/ash/accessibility/accessibility_controller_unittest.cc
+++ b/ash/accessibility/accessibility_controller_unittest.cc
@@ -10,7 +10,6 @@
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/magnifier/docked_magnifier_controller.h"
 #include "ash/public/cpp/ash_constants.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -20,7 +19,6 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_power_manager_client.h"
 #include "components/prefs/pref_service.h"
@@ -474,12 +472,6 @@
   AccessibilityControllerSigninTest() = default;
   ~AccessibilityControllerSigninTest() = default;
 
-  // AshTestBase:
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kDockedMagnifier);
-    NoSessionAshTestBase::SetUp();
-  }
-
   void SimulateLogin() {
     constexpr char kUserEmail[] = "user1@test.com";
     switch (GetParam()) {
@@ -498,8 +490,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(AccessibilityControllerSigninTest);
 };
 
diff --git a/ash/app_list/home_launcher_gesture_handler.cc b/ash/app_list/home_launcher_gesture_handler.cc
index c37b049..38fcc6f 100644
--- a/ash/app_list/home_launcher_gesture_handler.cc
+++ b/ash/app_list/home_launcher_gesture_handler.cc
@@ -468,24 +468,16 @@
     window2_->ResetOpacityAndTransform();
 
   if (is_final_state_show) {
-    ScopedAnimationDisabler disable(GetWindow1());
-    GetWindow1()->Hide();
-    wm::GetWindowState(GetWindow1())->Minimize();
+    wm::HideAndMinimizeWithoutAnimation(GetWindow1());
 
-    if (window2_) {
-      ScopedAnimationDisabler disable(GetWindow2());
-      GetWindow2()->Hide();
-      wm::GetWindowState(GetWindow2())->Minimize();
-    }
+    if (window2_)
+      wm::HideAndMinimizeWithoutAnimation(GetWindow2());
 
     // Minimize the hidden windows so they can be used normally with alt+tab
     // and overview. Minimize in reverse order to preserve mru ordering.
     std::reverse(hidden_windows_.begin(), hidden_windows_.end());
-    for (auto* window : hidden_windows_) {
-      ScopedAnimationDisabler disable(window);
-      window->Hide();
-      wm::GetWindowState(window)->Minimize();
-    }
+    for (auto* window : hidden_windows_)
+      wm::HideAndMinimizeWithoutAnimation(window);
   } else {
     // Reshow all windows previously hidden.
     for (auto* window : hidden_windows_) {
@@ -822,8 +814,7 @@
       if (window->IsVisible()) {
         hidden_windows_.push_back(window);
         window->AddObserver(this);
-        ScopedAnimationDisabler disable(window);
-        window->Hide();
+        wm::HideWithoutAnimation(window);
       }
     }
   }
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 0531c6e..5fd63ab 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -300,9 +300,7 @@
       bounds_animator_(this),
       page_flip_delay_in_ms_(kPageFlipDelayInMsFullscreen),
       pagination_animation_start_frame_number_(0),
-      view_structure_(this),
-      is_apps_grid_gap_feature_enabled_(
-          app_list_features::IsAppsGridGapFeatureEnabled()) {
+      view_structure_(this) {
   DCHECK(contents_view_);
   SetPaintToLayer(ui::LAYER_NOT_DRAWN);
   // Clip any icons that are outside the grid view's bounds. These icons would
@@ -679,7 +677,7 @@
   ClearDragState();
   UpdatePaging();
   AnimateToIdealBounds();
-  if (!cancel && IsAppsGridGapEnabled())
+  if (!cancel && !folder_delegate_)
     view_structure_.SaveToMetadata();
 
   if (folder_item_view) {
@@ -765,7 +763,7 @@
 
   // Add drag_view_ to the end of the view_model_.
   view_model_.Add(drag_view_, view_model_.view_size());
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Add(drag_view_, GetLastTargetIndex());
 
   drag_start_page_ = pagination_model_.selected_page();
@@ -867,7 +865,7 @@
     fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
 
   UpdateTilePadding();
-  CalculateIdealBounds();
+  CalculateIdealBoundsForFolder();
   for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
     if (view != drag_view_)
@@ -990,7 +988,7 @@
     view_model_.Add(view, view_model_.view_size());
     AddChildView(view);
   }
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
   UpdateColsAndRowsForFolder();
   UpdatePaging();
@@ -1010,7 +1008,7 @@
 }
 
 void AppsGridView::UpdatePaging() {
-  if (IsAppsGridGapEnabled()) {
+  if (!folder_delegate_) {
     pagination_model_.SetTotalPages(view_structure_.total_pages());
     return;
   }
@@ -1171,9 +1169,9 @@
   return gfx::Vector2d(x_offset, y_offset);
 }
 
-void AppsGridView::CalculateIdealBounds() {
-  if (IsAppsGridGapEnabled()) {
-    CalculateIdealBoundsWithGridGap();
+void AppsGridView::CalculateIdealBoundsForFolder() {
+  if (!folder_delegate_) {
+    CalculateIdealBounds();
     return;
   }
 
@@ -1208,7 +1206,7 @@
 void AppsGridView::AnimateToIdealBounds() {
   const gfx::Rect visible_bounds(GetVisibleBounds());
 
-  CalculateIdealBounds();
+  CalculateIdealBoundsForFolder();
   for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
     if (view == drag_view_)
@@ -1668,7 +1666,7 @@
   }
   ClearDragState();
   AnimateToIdealBounds();
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.SaveToMetadata();
 
   if (cancel_reparent) {
@@ -1916,7 +1914,7 @@
 
   // The same item index does not guarantee the same visual index, so move the
   // item visual index here.
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Move(item_view, target);
 
   // Reorder the app list item views in accordance with |view_model_|.
@@ -1969,7 +1967,7 @@
           CreateViewForItemAtIndex(folder_item_index);
       target_folder_view->SetBoundsRect(target_view_bounds);
       view_model_.Add(target_folder_view, target_model_index);
-      if (IsAppsGridGapEnabled())
+      if (!folder_delegate_)
         view_structure_.Add(target_folder_view, target_index);
 
       // If drag view is in front of the position where it will be moved to, we
@@ -1987,7 +1985,7 @@
   // Fade out the drag_view_ and delete it when animation ends.
   int drag_model_index = view_model_.GetIndexOfView(drag_view_);
   view_model_.Remove(drag_model_index);
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Remove(drag_view_);
   bounds_animator_.AnimateViewTo(drag_view_, drag_view_->bounds());
   bounds_animator_.SetAnimationDelegate(
@@ -2031,8 +2029,7 @@
 
     // Adjust |target_override| if it is beyond the deleted folder grid index in
     // the same page.
-    if (IsAppsGridGapEnabled() &&
-        target.page == deleted_folder_grid_index.page &&
+    if (!folder_delegate_ && target.page == deleted_folder_grid_index.page &&
         target.slot > deleted_folder_grid_index.slot) {
       --target_override.slot;
     }
@@ -2047,7 +2044,7 @@
     target_position = item_list_->item_at(target_item_index)->position();
   model_->MoveItemToFolderAt(reparent_item, "", target_position);
   view_model_.Move(current_model_index, target_model_index);
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Move(item_view, target_override);
   ReorderChildView(item_view, target_model_index);
 
@@ -2119,7 +2116,7 @@
           CreateViewForItemAtIndex(new_folder_index);
       new_folder_view->SetBoundsRect(target_rect);
       view_model_.Add(new_folder_view, target_model_index);
-      if (IsAppsGridGapEnabled())
+      if (!folder_delegate_)
         view_structure_.Add(new_folder_view, target_index);
       AddChildViewAt(new_folder_view, target_model_index);
     } else {
@@ -2134,7 +2131,7 @@
   // Fade out the drag_view_ and delete it when animation ends.
   int drag_model_index = view_model_.GetIndexOfView(drag_view_);
   view_model_.Remove(drag_model_index);
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Remove(drag_view_);
   bounds_animator_.AnimateViewTo(drag_view_, drag_view_->bounds());
   bounds_animator_.SetAnimationDelegate(
@@ -2183,7 +2180,7 @@
   AppListItemView* last_item_view = CreateViewForItemAtIndex(last_item_index);
   last_item_view->SetBoundsRect(folder_rect);
   view_model_.Add(last_item_view, target_model_index);
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.Add(last_item_view, target_index);
   AddChildViewAt(last_item_view, target_model_index);
 }
@@ -2202,7 +2199,7 @@
 void AppsGridView::DeleteItemViewAtIndex(int index, bool sanitize) {
   AppListItemView* item_view = GetItemViewAt(index);
   view_model_.Remove(index);
-  if (IsAppsGridGapEnabled()) {
+  if (!folder_delegate_) {
     view_structure_.Remove(item_view, sanitize /* clear_overflow */,
                            sanitize /* clear_empty_pages */);
   }
@@ -2279,7 +2276,7 @@
     view->SetVisible(model_->state_fullscreen() != AppListViewState::PEEKING);
   }
 
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
   UpdateColsAndRowsForFolder();
   UpdatePaging();
@@ -2294,7 +2291,7 @@
   if (!item->is_page_break())
     DeleteItemViewAtIndex(GetModelIndexOfItem(item), true /* sanitize */);
 
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
   UpdateColsAndRowsForFolder();
   UpdatePaging();
@@ -2320,7 +2317,7 @@
     ReorderChildView(view_model_.view_at(to_model_index), to_model_index);
   }
 
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
   UpdateColsAndRowsForFolder();
   UpdatePaging();
@@ -2469,12 +2466,8 @@
   }
 }
 
-bool AppsGridView::IsAppsGridGapEnabled() const {
-  return !folder_delegate_ && is_apps_grid_gap_feature_enabled_;
-}
-
 GridIndex AppsGridView::GetIndexFromModelIndex(int model_index) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetIndexFromModelIndex(model_index);
 
   const int tiles_in_page0 = TilesPerPage(0);
@@ -2488,7 +2481,7 @@
 }
 
 int AppsGridView::GetModelIndexFromIndex(const GridIndex& index) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetModelIndexFromIndex(index);
 
   if (index.page == 0)
@@ -2498,7 +2491,7 @@
 }
 
 GridIndex AppsGridView::GetLastTargetIndex() const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetLastTargetIndex();
 
   DCHECK_LT(0, view_model_.view_size());
@@ -2507,7 +2500,7 @@
 }
 
 GridIndex AppsGridView::GetLastTargetIndexOfPage(int page) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetLastTargetIndexOfPage(page);
 
   if (page == pagination_model_.total_pages() - 1)
@@ -2518,7 +2511,7 @@
 
 int AppsGridView::GetTargetModelIndexForMove(AppListItemView* moved_view,
                                              const GridIndex& index) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetTargetModelIndexForMove(moved_view, index);
 
   return GetModelIndexFromIndex(index);
@@ -2526,10 +2519,10 @@
 
 size_t AppsGridView::GetTargetItemIndexForMove(AppListItemView* moved_view,
                                                const GridIndex& index) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.GetTargetItemIndexForMove(moved_view, index);
 
-  // Model index is the same as item index when apps grid gap is disabled.
+  // Model index is the same as item index for folder.
   return GetModelIndexFromIndex(index);
 }
 
@@ -2540,7 +2533,7 @@
 }
 
 bool AppsGridView::IsValidReorderTargetIndex(const GridIndex& index) const {
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.IsValidReorderTargetIndex(index);
 
   return IsValidIndex(index);
@@ -2553,12 +2546,12 @@
   // If the user wants to drag an app to the next new page and has not done so
   // during the dragging session, then it is the right target because a new page
   // will be created in OnPageFlipTimer().
-  return IsAppsGridGapEnabled() && !extra_page_opened_ &&
+  return !folder_delegate_ && !extra_page_opened_ &&
          pagination_model_.total_pages() == page;
 }
 
-void AppsGridView::CalculateIdealBoundsWithGridGap() {
-  DCHECK(IsAppsGridGapEnabled());
+void AppsGridView::CalculateIdealBounds() {
+  DCHECK(!folder_delegate_);
 
   // |view_structure_| should only be updated at the end of drag. So make a
   // copy of it and only change the copy for calculating the ideal bounds of
@@ -2622,7 +2615,7 @@
 }
 
 int AppsGridView::GetTargetModelIndexFromItemIndex(size_t item_index) {
-  if (!IsAppsGridGapEnabled())
+  if (folder_delegate_)
     return item_index;
 
   CHECK(item_index <= item_list_->item_count());
@@ -2641,7 +2634,7 @@
 
   // Calculate the number of pages that have empty slots.
   int page_count = 0;
-  if (IsAppsGridGapEnabled()) {
+  if (!folder_delegate_) {
     const auto& pages = view_structure_.pages();
     for (size_t i = 0; i < pages.size(); ++i) {
       if (static_cast<int>(pages[i].size()) < TilesPerPage(i))
@@ -2653,8 +2646,7 @@
       item_num -= TilesPerPage(i);
     }
 
-    // Only last page allows gaps if it is not full when apps grid gap is
-    // disabled.
+    // Only last page allows gaps if it is not full for folder.
     if (item_num != 0)
       page_count = 1;
   }
@@ -2686,7 +2678,7 @@
   if (page < 0 || page >= pagination_model_.total_pages())
     return 0;
 
-  if (IsAppsGridGapEnabled())
+  if (!folder_delegate_)
     return view_structure_.items_on_page(page);
 
   if (page < pagination_model_.total_pages() - 1)
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index c772e39..fc24ffe 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -333,7 +333,8 @@
   // transition target page.
   const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
 
-  void CalculateIdealBounds();
+  // Calculates the item views' bounds for folder.
+  void CalculateIdealBoundsForFolder();
   void AnimateToIdealBounds();
 
   // Invoked when the given |view|'s current bounds and target bounds are on
@@ -523,11 +524,6 @@
   // Update number of columns and rows for apps within a folder.
   void UpdateColsAndRowsForFolder();
 
-  // Returns true if apps grid gap is enabled. If it is enabled, the user can
-  // drag an app to the next page without having to fill up the current
-  // page.
-  bool IsAppsGridGapEnabled() const;
-
   // Convert between the model index and the visual index. The model index
   // is the index of the item in AppListModel. The visual index is the Index
   // struct above with page/slot info of where to display the item.
@@ -561,8 +557,8 @@
   // Returns true if the page is the right target to flip to.
   bool IsValidPageFlipTarget(int page) const;
 
-  // Calculates the item views' bounds when apps grid gap is enabled.
-  void CalculateIdealBoundsWithGridGap();
+  // Calculates the item views' bounds for non-folder.
+  void CalculateIdealBounds();
 
   // Returns model index of the item view of the specified item.
   int GetModelIndexOfItem(const AppListItem* item);
@@ -700,17 +696,14 @@
   // The compositor frame number when animation starts.
   int pagination_animation_start_frame_number_;
 
-  // view structure used when apps grid gap is enabled.
+  // view structure used only for non-folder.
   PagedViewStructure view_structure_;
 
   // True if an extra page is opened after the user drags an app to the bottom
-  // of last page with intention to put it in a new page. This is only used when
-  // apps grid gap is enabled.
+  // of last page with intention to put it in a new page. This is only used for
+  // non-folder.
   bool extra_page_opened_ = false;
 
-  // True if the apps grid gap feature is enabled.
-  const bool is_apps_grid_gap_feature_enabled_;
-
   // Tile spacing between the tile views.
   int horizontal_tile_padding_ = 0;
   int vertical_tile_padding_ = 0;
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 6997a716..2cc8e0bb 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -140,32 +140,10 @@
   DISALLOW_COPY_AND_ASSIGN(TestSuggestedSearchResult);
 };
 
-struct TestParams {
-  bool is_rtl_enabled;
-  bool is_apps_grid_gap_enabled;
-};
-
-const TestParams kAppsGridViewTestParams[] = {
-    {false /* is_rtl_enabled */, false /* is_apps_grid_gap_enabled */},
-    {true, false},
-};
-
-const TestParams kAppsGridViewDragTestParams[] = {
-    {false /* is_rtl_enabled */, false /* is_apps_grid_gap_enabled */},
-    {true, false},
-    {false, true},
-    {true, true},
-};
-
-const TestParams kAppsGridGapTestParams[] = {
-    {false /* is_rtl_enabled */, true /* is_apps_grid_gap_enabled */},
-    {true, true},
-};
-
 }  // namespace
 
 class AppsGridViewTest : public views::ViewsTestBase,
-                         public testing::WithParamInterface<TestParams> {
+                         public testing::WithParamInterface<bool> {
  public:
   AppsGridViewTest() = default;
   ~AppsGridViewTest() override = default;
@@ -173,23 +151,11 @@
   // testing::Test overrides:
   void SetUp() override {
     AppListView::SetShortAnimationForTesting(true);
-    std::vector<base::Feature> enabled_features;
-    std::vector<base::Feature> disabled_features;
     if (testing::UnitTest::GetInstance()->current_test_info()->value_param()) {
-      is_rtl_ = GetParam().is_rtl_enabled;
+      is_rtl_ = GetParam();
       if (is_rtl_)
         base::i18n::SetICUDefaultLocale("he");
-
-      is_apps_grid_gap_enabled_ = GetParam().is_apps_grid_gap_enabled;
     }
-    if (is_apps_grid_gap_enabled_) {
-      enabled_features.emplace_back(
-          app_list_features::kEnableAppsGridGapFeature);
-    } else {
-      disabled_features.emplace_back(
-          app_list_features::kEnableAppsGridGapFeature);
-    }
-    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
     views::ViewsTestBase::SetUp();
     gfx::NativeView parent = GetContext();
     // Ensure that parent is big enough to show the full AppListView.
@@ -318,7 +284,6 @@
   std::unique_ptr<AppsGridViewTestApi> test_api_;
   bool is_rtl_ = false;
   bool test_with_fullscreen_ = true;
-  bool is_apps_grid_gap_enabled_ = false;
 
  private:
   // Restores the locale to default when destructor is called.
@@ -327,17 +292,9 @@
   // Used by AppListFolderView::UpdatePreferredBounds.
   keyboard::KeyboardController keyboard_controller_;
 
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
 };
 
-// Instantiate the Boolean which is used to toggle RTL in
-// the parameterized tests.
-INSTANTIATE_TEST_CASE_P(,
-                        AppsGridViewTest,
-                        testing::ValuesIn(kAppsGridViewTestParams));
-
 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
  public:
   TestAppsGridViewFolderDelegate() = default;
@@ -714,21 +671,7 @@
   EXPECT_FALSE(apps_grid_view_->IsSelectedView(app));
 }
 
-// Tests various dragging behaviors.
-class AppsGridViewDragTest : public AppsGridViewTest {
- public:
-  AppsGridViewDragTest() = default;
-  ~AppsGridViewDragTest() override = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AppsGridViewDragTest);
-};
-
-INSTANTIATE_TEST_CASE_P(,
-                        AppsGridViewDragTest,
-                        testing::ValuesIn(kAppsGridViewDragTestParams));
-
-TEST_P(AppsGridViewDragTest, MouseDragItemIntoFolder) {
+TEST_P(AppsGridViewTest, MouseDragItemIntoFolder) {
   size_t kTotalItems = 3;
   model_->PopulateApps(kTotalItems);
   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
@@ -775,7 +718,7 @@
   test_api_->LayoutToIdealBounds();
 }
 
-TEST_P(AppsGridViewDragTest, MouseDragMaxItemsInFolder) {
+TEST_P(AppsGridViewTest, MouseDragMaxItemsInFolder) {
   // Create and add a folder with |kMaxFolderItemsFullscreen - 1| items.
   const size_t kMaxItems =
       AppListConfig::instance().max_folder_items_per_page() *
@@ -822,7 +765,7 @@
 
 // Check that moving items around doesn't allow a drop to happen into a full
 // folder.
-TEST_P(AppsGridViewDragTest, MouseDragMaxItemsInFolderWithMovement) {
+TEST_P(AppsGridViewTest, MouseDragMaxItemsInFolderWithMovement) {
   // Create and add a folder with |kMaxFolderItemsFullscreen| in it.
   const size_t kMaxItems =
       AppListConfig::instance().max_folder_items_per_page() *
@@ -875,7 +818,7 @@
 }
 
 // Test reordering items via dragging.
-TEST_P(AppsGridViewDragTest, MouseDragItemReorder) {
+TEST_P(AppsGridViewTest, MouseDragItemReorder) {
   // The default layout is 5x4, populate 7 apps so that we have second row to
   // test dragging item to second row.
   model_->PopulateApps(7);
@@ -950,7 +893,7 @@
   TestAppListItemViewIndice();
 }
 
-TEST_P(AppsGridViewDragTest, MouseDragFolderReorder) {
+TEST_P(AppsGridViewTest, MouseDragFolderReorder) {
   size_t kTotalItems = 2;
   model_->CreateAndPopulateFolderWithApps(kTotalItems);
   model_->PopulateAppWithId(kTotalItems);
@@ -975,7 +918,7 @@
   TestAppListItemViewIndice();
 }
 
-TEST_P(AppsGridViewDragTest, MouseDragWithCancelDeleteAddItem) {
+TEST_P(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
   size_t kTotalItems = 4;
   model_->PopulateApps(kTotalItems);
   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
@@ -1008,7 +951,7 @@
   test_api_->LayoutToIdealBounds();
 }
 
-TEST_P(AppsGridViewDragTest, MouseDragFlipPage) {
+TEST_P(AppsGridViewTest, MouseDragFlipPage) {
   apps_grid_view_->set_page_flip_delay_in_ms_for_testing(10);
   GetPaginationModel()->SetTransitionDurations(10, 10);
 
@@ -1033,15 +976,10 @@
     page_flip_waiter.Wait();
   }
 
-  if (is_apps_grid_gap_enabled_) {
-    // When apps grid gap is enabled, the user can drag an item to an extra page
-    // created at the end.
-    EXPECT_EQ("1,2,3", page_flip_waiter.selected_pages());
-    EXPECT_EQ(3, GetPaginationModel()->selected_page());
-  } else {
-    EXPECT_EQ("1,2", page_flip_waiter.selected_pages());
-    EXPECT_EQ(2, GetPaginationModel()->selected_page());
-  }
+  // When apps grid gap is enabled, the user can drag an item to an extra page
+  // created at the end.
+  EXPECT_EQ("1,2,3", page_flip_waiter.selected_pages());
+  EXPECT_EQ(3, GetPaginationModel()->selected_page());
 
   // Cancel drag and put the dragged view back to its ideal position so that
   // the next drag would pick it up.
@@ -1064,7 +1002,7 @@
   apps_grid_view_->EndDrag(true);
 }
 
-TEST_F(AppsGridViewDragTest, UpdateFolderBackgroundOnCancelDrag) {
+TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
   const int kTotalItems = 4;
   TestAppsGridViewFolderDelegate folder_delegate;
   apps_grid_view_->set_folder_delegate(&folder_delegate);
@@ -1091,8 +1029,6 @@
 
   // testing::Test overrides:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {app_list_features::kEnableAppsGridGapFeature}, {});
     AppsGridViewTest::SetUp();
     apps_grid_view_->set_page_flip_delay_in_ms_for_testing(10);
     GetPaginationModel()->SetTransitionDurations(10, 10);
@@ -1146,15 +1082,9 @@
   std::unique_ptr<PageFlipWaiter> page_flip_waiter_;
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(AppsGridGapTest);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        AppsGridGapTest,
-                        testing::ValuesIn(kAppsGridGapTestParams));
-
 TEST_P(AppsGridGapTest, MoveAnItemToNewEmptyPage) {
   const int kApps = 2;
   model_->PopulateApps(kApps);
diff --git a/ash/magnifier/docked_magnifier_controller_unittest.cc b/ash/magnifier/docked_magnifier_controller_unittest.cc
index dd1bd14b..de0d191b 100644
--- a/ash/magnifier/docked_magnifier_controller_unittest.cc
+++ b/ash/magnifier/docked_magnifier_controller_unittest.cc
@@ -12,7 +12,6 @@
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/host/ash_window_tree_host.h"
 #include "ash/magnifier/magnifier_test_utils.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/interfaces/docked_magnifier_controller.mojom.h"
@@ -27,7 +26,6 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/command_line.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/session_manager_types.h"
 #include "ui/aura/window.h"
@@ -72,9 +70,6 @@
 
   // AshTestBase:
   void SetUp() override {
-    // Explicitly enable the Docked Magnifier feature for the tests.
-    scoped_feature_list_.InitAndEnableFeature(features::kDockedMagnifier);
-
     // Explicitly enable --ash-constrain-pointer-to-root to be able to test
     // mouse cursor confinement outside the magnifier viewport.
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -140,8 +135,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(DockedMagnifierTest);
 };
 
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index b88c914..3a79043a 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -316,11 +316,9 @@
   registry->AddInterface(
       base::BindRepeating(&BindDisplayOutputProtectionRequestOnMainThread),
       main_thread_task_runner);
-  if (features::IsDockedMagnifierEnabled()) {
-    registry->AddInterface(
-        base::BindRepeating(&BindDockedMagnifierControllerRequestOnMainThread),
-        main_thread_task_runner);
-  }
+  registry->AddInterface(
+      base::BindRepeating(&BindDockedMagnifierControllerRequestOnMainThread),
+      main_thread_task_runner);
   registry->AddInterface(
       base::BindRepeating(&BindEventRewriterControllerRequestOnMainThread),
       main_thread_task_runner);
@@ -348,11 +346,9 @@
   registry->AddInterface(
       base::BindRepeating(&BindNewWindowControllerRequestOnMainThread),
       main_thread_task_runner);
-  if (features::IsNightLightEnabled()) {
     registry->AddInterface(
         base::BindRepeating(&BindNightLightControllerRequestOnMainThread),
         main_thread_task_runner);
-  }
   registry->AddInterface(
       base::BindRepeating(&BindNoteTakingControllerRequestOnMainThread),
       main_thread_task_runner);
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index 5cba0a4..54bcea5 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -24,8 +24,6 @@
     "HomeLauncherGestures", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableSettingsShortcutSearch{
     "EnableSettingsShortcutSearch", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kEnableAppsGridGapFeature{"EnableAppsGridGapFeature",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableZeroStateSuggestions{
     "EnableZeroStateSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableAppListSearchAutocomplete{
@@ -63,10 +61,6 @@
   return base::FeatureList::IsEnabled(kEnableSettingsShortcutSearch);
 }
 
-bool IsAppsGridGapFeatureEnabled() {
-  return base::FeatureList::IsEnabled(kEnableAppsGridGapFeature);
-}
-
 bool IsZeroStateSuggestionsEnabled() {
   return base::FeatureList::IsEnabled(kEnableZeroStateSuggestions);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 58c5dae..133ae98 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -42,9 +42,6 @@
 // Enables the Settings shortcut search.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableSettingsShortcutSearch;
 
-// Enables the apps grid gap feature.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppsGridGapFeature;
-
 // Enables the feature to display zero state suggestions.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableZeroStateSuggestions;
 
@@ -61,7 +58,6 @@
 bool ASH_PUBLIC_EXPORT IsAppDataSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsHomeLauncherGesturesEnabled();
 bool ASH_PUBLIC_EXPORT IsSettingsShortcutSearchEnabled();
-bool ASH_PUBLIC_EXPORT IsAppsGridGapFeatureEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateSuggestionsEnabled();
 bool ASH_PUBLIC_EXPORT IsAppListSearchAutocompleteEnabled();
 bool ASH_PUBLIC_EXPORT IsAppSearchResultRankerEnabled();
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 8e98036..06eb2e66 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -60,10 +60,6 @@
 const base::Feature kSupervisedUserDeprecationNotice{
     "SupervisedUserDeprecationNotice", base::FEATURE_ENABLED_BY_DEFAULT};
 
-bool IsDockedMagnifierEnabled() {
-  return base::FeatureList::IsEnabled(kDockedMagnifier);
-}
-
 bool IsLockScreenNotificationsEnabled() {
   return base::FeatureList::IsEnabled(kLockScreenNotifications);
 }
@@ -77,10 +73,6 @@
       kLockScreenHideSensitiveNotificationsSupport);
 }
 
-bool IsNightLightEnabled() {
-  return base::FeatureList::IsEnabled(kNightLight);
-}
-
 bool IsNotificationExpansionAnimationEnabled() {
   return base::FeatureList::IsEnabled(kNotificationExpansionAnimation);
 }
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 6192cea..8cc65f9 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -72,8 +72,6 @@
 // Enables the Supervised User Deprecation notices.
 ASH_PUBLIC_EXPORT extern const base::Feature kSupervisedUserDeprecationNotice;
 
-ASH_PUBLIC_EXPORT bool IsDockedMagnifierEnabled();
-
 ASH_PUBLIC_EXPORT bool IsKeyboardShortcutViewerAppEnabled();
 
 ASH_PUBLIC_EXPORT bool IsLockScreenNotificationsEnabled();
@@ -82,8 +80,6 @@
 
 ASH_PUBLIC_EXPORT bool IsLockScreenHideSensitiveNotificationsSupported();
 
-ASH_PUBLIC_EXPORT bool IsNightLightEnabled();
-
 ASH_PUBLIC_EXPORT bool IsNotificationExpansionAnimationEnabled();
 
 ASH_PUBLIC_EXPORT bool IsNotificationScrollBarEnabled();
diff --git a/ash/resources/BUILD.gn b/ash/resources/BUILD.gn
index 31dfd84..7e6acf4 100644
--- a/ash/resources/BUILD.gn
+++ b/ash/resources/BUILD.gn
@@ -76,9 +76,13 @@
 ash_test_resources("with_content_100_percent") {
   percent = "100"
   sources = [
+    "$root_gen_dir/content/app/strings/content_strings_en-US.pak",
     "$root_gen_dir/content/content_resources.pak",
+    "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
   ]
   deps = [
     "//content:resources",
+    "//content/app/strings",
+    "//third_party/blink/public:scaled_resources_100_percent",
   ]
 }
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 1f39ad8..c772886 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -2248,7 +2248,7 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfViewMenuTest);
 };
 
-INSTANTIATE_TEST_CASE_P(, ShelfViewMenuTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(, ShelfViewMenuTest, testing::Bool());
 
 // Tests that menu anchor points are aligned with the shelf button bounds.
 TEST_P(ShelfViewMenuTest, ShelfViewMenuAnchorPoint) {
@@ -2414,10 +2414,10 @@
   CheckAllItemsAreInBounds();
 }
 
-INSTANTIATE_TEST_CASE_P(LtrRtl, ShelfViewTextDirectionTest, testing::Bool());
-INSTANTIATE_TEST_CASE_P(VisibleBounds,
-                        ShelfViewVisibleBoundsTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(LtrRtl, ShelfViewTextDirectionTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(VisibleBounds,
+                         ShelfViewVisibleBoundsTest,
+                         testing::Bool());
 
 namespace {
 
@@ -3214,7 +3214,7 @@
   DISALLOW_COPY_AND_ASSIGN(OverflowButtonTextDirectionTest);
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     /* prefix intentionally left blank due to only one parameterization */,
     OverflowButtonTextDirectionTest,
     testing::Bool());
diff --git a/ash/shell.cc b/ash/shell.cc
index e3915c4..ba07f4b47 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -492,12 +492,10 @@
 }
 
 DockedMagnifierController* Shell::docked_magnifier_controller() {
-  DCHECK(features::IsDockedMagnifierEnabled());
   return docked_magnifier_controller_.get();
 }
 
 NightLightController* Shell::night_light_controller() {
-  DCHECK(features::IsNightLightEnabled());
   return night_light_controller_.get();
 }
 
@@ -1062,8 +1060,7 @@
 
   // Night Light depends on the display manager, the display color manager, and
   // aura::Env, so initialize it after all have been initialized.
-  if (features::IsNightLightEnabled())
-    night_light_controller_ = std::make_unique<NightLightController>();
+  night_light_controller_ = std::make_unique<NightLightController>();
 
   // The WindowModalityController needs to be at the front of the input event
   // pretarget handler list to ensure that it processes input events when modal
@@ -1189,10 +1186,7 @@
 
   high_contrast_controller_.reset(new HighContrastController);
 
-  if (features::IsDockedMagnifierEnabled()) {
-    docked_magnifier_controller_ =
-        std::make_unique<DockedMagnifierController>();
-  }
+  docked_magnifier_controller_ = std::make_unique<DockedMagnifierController>();
 
   video_detector_ = std::make_unique<VideoDetector>();
 
diff --git a/ash/shell/content/client/shell_browser_main_parts.cc b/ash/shell/content/client/shell_browser_main_parts.cc
index da86a59e..8860ec0 100644
--- a/ash/shell/content/client/shell_browser_main_parts.cc
+++ b/ash/shell/content/client/shell_browser_main_parts.cc
@@ -12,7 +12,10 @@
 #include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/keyboard/test_keyboard_ui.h"
 #include "ash/login_status.h"
+#include "ash/public/cpp/mus_property_mirror_ash.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
+#include "ash/shell/content/embedded_browser.h"
 #include "ash/shell/example_session_controller_client.h"
 #include "ash/shell/shell_delegate_impl.h"
 #include "ash/shell/shell_views_delegate.h"
@@ -24,6 +27,7 @@
 #include "base/i18n/icu_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
@@ -31,6 +35,8 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_policy_controller.h"
 #include "components/exo/file_helper.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/context_factory.h"
 #include "content/public/browser/gpu_interface_provider_factory.h"
 #include "content/public/common/content_switches.h"
@@ -41,13 +47,17 @@
 #include "net/base/net_module.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/ime/test_ime_driver/public/mojom/constants.mojom.h"
+#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/aura/env.h"
+#include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_paths.h"
 #include "ui/compositor/compositor.h"
 #include "ui/views/examples/examples_window_with_content.h"
+#include "ui/views/mus/mus_client.h"
 #include "ui/wm/core/wm_state.h"
 
 namespace ash {
@@ -62,6 +72,10 @@
 
 void ShellBrowserMainParts::PostMainMessageLoopStart() {
   chromeos::DBusThreadManager::Initialize(chromeos::DBusThreadManager::kShared);
+
+  // WindowTreeClient needs to do some shutdown while the IO thread is alive.
+  if (mus_client_)
+    mus_client_->window_tree_client()->OnEarlyShutdown();
 }
 
 void ShellBrowserMainParts::ToolkitInitialized() {
@@ -89,6 +103,22 @@
   service_manager::Connector* const connector =
       content::ServiceManagerConnection::GetForProcess()->GetConnector();
 
+  if (features::IsUsingWindowService()) {
+    connector->WarmService(
+        service_manager::ServiceFilter::ByName(ws::mojom::kServiceName));
+
+    views::MusClient::InitParams params;
+    params.connector = connector;
+    params.io_task_runner = base::CreateSingleThreadTaskRunnerWithTraits(
+        {content::BrowserThread::IO});
+    params.create_wm_state = false;
+    params.running_in_ws_process = features::IsSingleProcessMash();
+    mus_client_ = std::make_unique<views::MusClient>(params);
+    ash::RegisterWindowProperties(mus_client_->property_converter());
+    mus_client_->SetMusPropertyMirror(
+        std::make_unique<ash::MusPropertyMirrorAsh>());
+  }
+
   ui::MaterialDesignController::Initialize();
   ash::ShellInitParams init_params;
   init_params.delegate = std::make_unique<ash::shell::ShellDelegateImpl>();
@@ -109,9 +139,12 @@
   window_watcher_ = std::make_unique<WindowWatcher>();
 
   ash::shell::InitWindowTypeLauncher(
-      base::Bind(&views::examples::ShowExamplesWindowWithContent,
-                 base::Passed(base::OnceClosure()),
-                 base::Unretained(browser_context_.get()), nullptr));
+      base::BindRepeating(&views::examples::ShowExamplesWindowWithContent,
+                          base::Passed(base::OnceClosure()),
+                          base::Unretained(browser_context_.get()), nullptr),
+      base::BindRepeating(&EmbeddedBrowser::Create,
+                          base::Unretained(browser_context_.get()),
+                          GURL("https://www.google.com")));
 
   ash::Shell::GetPrimaryRootWindow()->GetHost()->Show();
 
@@ -148,7 +181,10 @@
 }
 
 bool ShellBrowserMainParts::MainMessageLoopRun(int* result_code) {
-  base::RunLoop().Run();
+  base::RunLoop run_loop;
+  example_session_controller_client_->set_quit_closure(
+      run_loop.QuitWhenIdleClosure());
+  run_loop.Run();
   return true;
 }
 
diff --git a/ash/shell/content/client/shell_browser_main_parts.h b/ash/shell/content/client/shell_browser_main_parts.h
index 9e2afaf78..7286e929 100644
--- a/ash/shell/content/client/shell_browser_main_parts.h
+++ b/ash/shell/content/client/shell_browser_main_parts.h
@@ -20,6 +20,7 @@
 }
 
 namespace views {
+class MusClient;
 class ViewsDelegate;
 }
 
@@ -58,6 +59,7 @@
   std::unique_ptr<wm::WMState> wm_state_;
   std::unique_ptr<ExampleSessionControllerClient>
       example_session_controller_client_;
+  std::unique_ptr<views::MusClient> mus_client_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellBrowserMainParts);
 };
diff --git a/ash/shell/content/embedded_browser.cc b/ash/shell/content/embedded_browser.cc
new file mode 100644
index 0000000..a2af3c8
--- /dev/null
+++ b/ash/shell/content/embedded_browser.cc
@@ -0,0 +1,125 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/shell/content/embedded_browser.h"
+
+#include "ash/shell.h"
+#include "ash/ws/window_service_owner.h"
+#include "base/bind_helpers.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/unguessable_token.h"
+#include "content/public/browser/web_contents.h"
+#include "services/ws/remote_view_host/server_remote_view_host.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/native/native_view_host.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/mus/remote_view/remote_view_provider.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "url/gurl.h"
+
+namespace ash {
+namespace shell {
+
+namespace {
+
+// Simulate a widget created on Window Service side to host a native window.
+// For classic, it is done by NativeViewHost. For single process mash,
+// it uses ServerRemoteViewHost. To implement for mash, the code should be
+// moved to ash process somehow and invoked over mojo.
+class HostWidget : public views::WidgetDelegateView {
+ public:
+  // Creates HostWidget to host remote content represented by |token| for mash.
+  static void CreateFromToken(const base::UnguessableToken& token) {
+    // HostWidget deletes itself when underlying widget closes.
+    new HostWidget(nullptr, token);
+  }
+
+  // Creates HostWidget that hosts |native_window| directly for classic.
+  static void CreateFromNativeWindow(gfx::NativeWindow native_window) {
+    // HostWidget deletes itself when underlying widget closes.
+    new HostWidget(native_window, base::nullopt);
+  }
+
+ private:
+  HostWidget(gfx::NativeWindow native_window,
+             base::Optional<base::UnguessableToken> token) {
+    // Note this does not work under multi process mash.
+    DCHECK(!features::IsMultiProcessMash());
+
+    SetLayoutManager(std::make_unique<views::FillLayout>());
+
+    auto* widget = new views::Widget;
+    views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
+    params.context = Shell::GetPrimaryRootWindow();
+    params.bounds = gfx::Rect(20, 0, 800, 600);
+    params.delegate = this;
+    widget->Init(params);
+    widget->Show();
+
+    if (native_window) {
+      title_ = base::ASCIIToUTF16("Classic: Embed by NativeViewHost");
+      auto* host = new views::NativeViewHost();
+      AddChildView(host);
+      host->Attach(native_window);
+    } else {
+      title_ = base::ASCIIToUTF16("SPM: Embed by ServerRemoteViewHost");
+      auto* host = new ws::ServerRemoteViewHost(
+          ash::Shell::Get()->window_service_owner()->window_service());
+      AddChildView(host);
+      host->EmbedUsingToken(token.value(), 0, base::DoNothing());
+    }
+
+    Layout();
+  }
+
+  // views::WidgetDelegateView:
+  base::string16 GetWindowTitle() const override { return title_; }
+
+  base::string16 title_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostWidget);
+};
+
+}  // namespace
+
+EmbeddedBrowser::EmbeddedBrowser(content::BrowserContext* context,
+                                 const GURL& url) {
+  contents_ =
+      content::WebContents::Create(content::WebContents::CreateParams(context));
+  contents_->GetController().LoadURLWithParams(
+      content::NavigationController::LoadURLParams(url));
+  contents_->GetNativeView()->Show();
+
+  if (!features::IsUsingWindowService()) {
+    HostWidget::CreateFromNativeWindow(contents_->GetNativeView());
+    return;
+  }
+
+  remote_view_provider_ =
+      std::make_unique<views::RemoteViewProvider>(contents_->GetNativeView());
+  remote_view_provider_->GetEmbedToken(
+      base::BindOnce([](const base::UnguessableToken& token) {
+        HostWidget::CreateFromToken(token);
+      }));
+}
+
+EmbeddedBrowser::~EmbeddedBrowser() = default;
+
+// static
+void EmbeddedBrowser::Create(content::BrowserContext* context,
+                             const GURL& url) {
+  // EmbeddedBrowser deletes itself when the hosting widget is closed.
+  new EmbeddedBrowser(context, url);
+}
+
+void EmbeddedBrowser::OnUnembed() {
+  delete this;
+}
+
+}  // namespace shell
+}  // namespace ash
diff --git a/ash/shell/content/embedded_browser.h b/ash/shell/content/embedded_browser.h
new file mode 100644
index 0000000..da9b4cf
--- /dev/null
+++ b/ash/shell/content/embedded_browser.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SHELL_CONTENT_EMBEDDED_BROWSER_H_
+#define ASH_SHELL_CONTENT_EMBEDDED_BROWSER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}  // namespace content
+
+namespace views {
+class RemoteViewProvider;
+}
+
+namespace ash {
+namespace shell {
+
+// Exercises ServerRemoteViewHost to embed a content::WebContents.
+class EmbeddedBrowser {
+ public:
+  EmbeddedBrowser(content::BrowserContext* context, const GURL& url);
+  ~EmbeddedBrowser();
+
+  // Factory.
+  static void Create(content::BrowserContext* context, const GURL& url);
+
+ private:
+  // Callback invoked when the embedding is broken.
+  void OnUnembed();
+
+  std::unique_ptr<content::WebContents> contents_;
+  std::unique_ptr<views::RemoteViewProvider> remote_view_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddedBrowser);
+};
+
+}  // namespace shell
+}  // namespace ash
+
+#endif  // ASH_SHELL_CONTENT_EMBEDDED_BROWSER_H_
diff --git a/ash/shell/example_session_controller_client.cc b/ash/shell/example_session_controller_client.cc
index aebf375a..de224e4 100644
--- a/ash/shell/example_session_controller_client.cc
+++ b/ash/shell/example_session_controller_client.cc
@@ -54,5 +54,10 @@
   Shell::Get()->UpdateShelfVisibility();
 }
 
+void ExampleSessionControllerClient::RequestSignOut() {
+  DCHECK(quit_closure_);
+  std::move(quit_closure_).Run();
+}
+
 }  // namespace shell
 }  // namespace ash
diff --git a/ash/shell/example_session_controller_client.h b/ash/shell/example_session_controller_client.h
index 952a5300..8b5a023 100644
--- a/ash/shell/example_session_controller_client.h
+++ b/ash/shell/example_session_controller_client.h
@@ -5,7 +5,10 @@
 #ifndef ASH_SHELL_EXAMPLE_SESSION_CONTROLLER_CLIENT_H_
 #define ASH_SHELL_EXAMPLE_SESSION_CONTROLLER_CLIENT_H_
 
+#include <utility>
+
 #include "ash/session/test_session_controller_client.h"
+#include "base/callback.h"
 #include "base/macros.h"
 
 namespace ash {
@@ -23,11 +26,18 @@
 
   void Initialize();
 
+  void set_quit_closure(base::OnceClosure quit_closure) {
+    quit_closure_ = std::move(quit_closure);
+  }
+
   // TestSessionControllerClient
   void RequestLockScreen() override;
   void UnlockScreen() override;
+  void RequestSignOut() override;
 
  private:
+  base::OnceClosure quit_closure_;
+
   DISALLOW_COPY_AND_ASSIGN(ExampleSessionControllerClient);
 };
 
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index 6c87b31..eefde08 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -163,17 +163,21 @@
 
 }  // namespace
 
-void InitWindowTypeLauncher(const base::Closure& show_views_examples_callback) {
+void InitWindowTypeLauncher(
+    base::RepeatingClosure show_views_examples_callback,
+    base::RepeatingClosure create_embedded_browser_callback) {
   views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
-      new WindowTypeLauncher(show_views_examples_callback),
-      Shell::GetPrimaryRootWindow(), gfx::Rect(120, 150, 300, 410));
+      new WindowTypeLauncher(show_views_examples_callback,
+                             create_embedded_browser_callback),
+      Shell::GetPrimaryRootWindow(), gfx::Rect(120, 120, 300, 410));
   widget->GetNativeView()->SetName("WindowTypeLauncher");
   ::wm::SetShadowElevation(widget->GetNativeView(), kWindowShadowElevation);
   widget->Show();
 }
 
 WindowTypeLauncher::WindowTypeLauncher(
-    const base::Closure& show_views_examples_callback)
+    base::RepeatingClosure show_views_examples_callback,
+    base::RepeatingClosure create_embedded_browser_callback)
     : create_button_(
           MdTextButton::Create(this, base::ASCIIToUTF16("Create Window"))),
       create_nonresizable_button_(MdTextButton::Create(
@@ -207,7 +211,11 @@
       show_web_notification_(MdTextButton::Create(
           this,
           base::ASCIIToUTF16("Show a web/app notification"))),
-      show_views_examples_callback_(show_views_examples_callback) {
+      embedded_browser_button_(
+          MdTextButton::Create(this, base::ASCIIToUTF16("Embedded Browser"))),
+      show_views_examples_callback_(std::move(show_views_examples_callback)),
+      create_embedded_browser_callback_(
+          std::move(create_embedded_browser_callback)) {
   views::GridLayout* layout =
       SetLayoutManager(std::make_unique<views::GridLayout>(this));
   SetBorder(views::CreateEmptyBorder(gfx::Insets(5)));
@@ -226,6 +234,7 @@
   AddViewToLayout(layout, examples_button_);
   AddViewToLayout(layout, show_hide_window_button_);
   AddViewToLayout(layout, show_web_notification_);
+  AddViewToLayout(layout, embedded_browser_button_);
   set_context_menu_controller(this);
 }
 
@@ -265,6 +274,8 @@
     ToplevelWindow::CreateToplevelWindow(params);
   } else if (sender == create_nonresizable_button_) {
     ToplevelWindow::CreateToplevelWindow(ToplevelWindow::CreateParams());
+  } else if (sender == embedded_browser_button_) {
+    create_embedded_browser_callback_.Run();
   } else if (sender == bubble_button_) {
     CreatePointyBubble(sender);
   } else if (sender == lock_button_) {
@@ -304,7 +315,8 @@
 void WindowTypeLauncher::ExecuteCommand(int id, int event_flags) {
   switch (id) {
     case COMMAND_NEW_WINDOW:
-      InitWindowTypeLauncher(show_views_examples_callback_);
+      InitWindowTypeLauncher(show_views_examples_callback_,
+                             create_embedded_browser_callback_);
       break;
     case COMMAND_TOGGLE_FULLSCREEN:
       GetWidget()->SetFullscreen(!GetWidget()->IsFullscreen());
diff --git a/ash/shell/window_type_launcher.h b/ash/shell/window_type_launcher.h
index 73b42c2..15321c7 100644
--- a/ash/shell/window_type_launcher.h
+++ b/ash/shell/window_type_launcher.h
@@ -25,7 +25,11 @@
 // is Run() when the user clicks on the views examples button. This should
 // be bound to either views::examples::ShowExamplesWindow() or
 // views::examples::ShowExamplesWindowWithContent().
-void InitWindowTypeLauncher(const base::Closure& show_views_examples_callback);
+// |create_embedded_browser_callback| is Run when user clicks the "embedded
+// browser" button.
+void InitWindowTypeLauncher(
+    base::RepeatingClosure show_views_examples_callback,
+    base::RepeatingClosure create_embedded_browser_callback);
 
 // The contents view/delegate of a window that shows some buttons that create
 // various window types.
@@ -34,8 +38,8 @@
                            public views::MenuDelegate,
                            public views::ContextMenuController {
  public:
-  explicit WindowTypeLauncher(
-      const base::Closure& show_views_examples_callback);
+  WindowTypeLauncher(base::RepeatingClosure show_views_examples_callback,
+                     base::RepeatingClosure create_embedded_browser_callback);
   ~WindowTypeLauncher() override;
 
  private:
@@ -79,8 +83,12 @@
   views::Button* examples_button_;
   views::Button* show_hide_window_button_;
   views::Button* show_web_notification_;
+  views::Button* embedded_browser_button_;
+
   std::unique_ptr<views::MenuRunner> menu_runner_;
-  base::Closure show_views_examples_callback_;
+
+  base::RepeatingClosure show_views_examples_callback_;
+  base::RepeatingClosure create_embedded_browser_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowTypeLauncher);
 };
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc
index 0fc48430..9f77bb92 100644
--- a/ash/system/accessibility/tray_accessibility.cc
+++ b/ash/system/accessibility/tray_accessibility.cc
@@ -90,12 +90,10 @@
   TrayPopupUtils::UpdateCheckMarkVisibility(screen_magnifier_view_,
                                             screen_magnifier_enabled_);
 
-  if (features::IsDockedMagnifierEnabled()) {
-    docked_magnifier_enabled_ =
-        Shell::Get()->docked_magnifier_controller()->GetEnabled();
-    TrayPopupUtils::UpdateCheckMarkVisibility(docked_magnifier_view_,
-                                              docked_magnifier_enabled_);
-  }
+  docked_magnifier_enabled_ =
+      Shell::Get()->docked_magnifier_controller()->GetEnabled();
+  TrayPopupUtils::UpdateCheckMarkVisibility(docked_magnifier_view_,
+                                            docked_magnifier_enabled_);
 
   autoclick_enabled_ = controller->autoclick_enabled();
   TrayPopupUtils::UpdateCheckMarkVisibility(autoclick_view_,
@@ -173,7 +171,6 @@
           IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER),
       screen_magnifier_enabled_);
 
-  if (features::IsDockedMagnifierEnabled()) {
     docked_magnifier_enabled_ =
         Shell::Get()->docked_magnifier_controller()->GetEnabled();
     docked_magnifier_view_ = AddScrollListCheckableItem(
@@ -181,7 +178,6 @@
         l10n_util::GetStringUTF16(
             IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DOCKED_MAGNIFIER),
         docked_magnifier_enabled_);
-  }
 
   autoclick_enabled_ = controller->autoclick_enabled();
   autoclick_view_ = AddScrollListCheckableItem(
@@ -275,8 +271,7 @@
                      ? UserMetricsAction("StatusArea_MagnifierDisabled")
                      : UserMetricsAction("StatusArea_MagnifierEnabled"));
     delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled());
-  } else if (features::IsDockedMagnifierEnabled() &&
-             view == docked_magnifier_view_) {
+  } else if (view == docked_magnifier_view_) {
     auto* docked_magnifier_controller =
         Shell::Get()->docked_magnifier_controller();
     const bool new_state = !docked_magnifier_controller->GetEnabled();
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc
index d2908ce..4d9b6510 100644
--- a/ash/system/night_light/night_light_controller_unittest.cc
+++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "ash/display/cursor_window_controller.h"
 #include "ash/display/window_tree_host_manager.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/session_types.h"
 #include "ash/root_window_controller.h"
@@ -27,7 +26,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/pattern.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/prefs/pref_service.h"
 #include "ui/compositor/layer.h"
 #include "ui/display/manager/display_change_observer.h"
@@ -159,9 +157,6 @@
 
   // AshTestBase:
   void SetUp() override {
-    // Explicitly enable the NightLight feature for the tests.
-    scoped_feature_list_.InitAndEnableFeature(features::kNightLight);
-
     NoSessionAshTestBase::SetUp();
     CreateTestUserSessions();
 
@@ -189,8 +184,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   TestDelegate* delegate_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(NightLightTest);
diff --git a/ash/system/night_light/night_light_feature_pod_controller.cc b/ash/system/night_light/night_light_feature_pod_controller.cc
index 31739d8..e5c3d3b 100644
--- a/ash/system/night_light/night_light_feature_pod_controller.cc
+++ b/ash/system/night_light/night_light_feature_pod_controller.cc
@@ -4,7 +4,6 @@
 
 #include "ash/system/night_light/night_light_feature_pod_controller.h"
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -33,7 +32,6 @@
   button_ = new FeaturePodButton(this);
   button_->SetVectorIcon(kUnifiedMenuNightLightIcon);
   button_->SetVisible(
-      features::IsNightLightEnabled() &&
       Shell::Get()->session_controller()->ShouldEnableSettings());
   button_->SetLabel(
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_BUTTON_LABEL));
@@ -46,7 +44,6 @@
 }
 
 void NightLightFeaturePodController::OnIconPressed() {
-  DCHECK(features::IsNightLightEnabled());
   Shell::Get()->night_light_controller()->Toggle();
   UpdateButton();
 
@@ -60,7 +57,6 @@
 }
 
 void NightLightFeaturePodController::OnLabelPressed() {
-  DCHECK(features::IsNightLightEnabled());
   if (TrayPopupUtils::CanOpenWebUISettings()) {
     base::RecordAction(
         base::UserMetricsAction("StatusArea_NightLight_Settings"));
@@ -74,10 +70,7 @@
 }
 
 void NightLightFeaturePodController::UpdateButton() {
-  if (!features::IsNightLightEnabled())
-    return;
-
-  bool is_enabled = Shell::Get()->night_light_controller()->GetEnabled();
+  const bool is_enabled = Shell::Get()->night_light_controller()->GetEnabled();
   button_->SetToggled(is_enabled);
   button_->SetSubLabel(l10n_util::GetStringUTF16(
       is_enabled ? IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ON_STATE
diff --git a/ash/system/night_light/night_light_toggle_button.cc b/ash/system/night_light/night_light_toggle_button.cc
index efb6b6e..3bdcdcb 100644
--- a/ash/system/night_light/night_light_toggle_button.cc
+++ b/ash/system/night_light/night_light_toggle_button.cc
@@ -4,7 +4,6 @@
 
 #include "ash/system/night_light/night_light_toggle_button.h"
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/night_light/night_light_controller.h"
@@ -56,7 +55,6 @@
 }
 
 void NightLightToggleButton::Toggle() {
-  DCHECK(features::IsNightLightEnabled());
   Shell::Get()->night_light_controller()->Toggle();
   Update();
   NotifyAccessibilityEvent(ax::mojom::Event::kAriaAttributeChanged, true);
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 27528ad..8d6aff8 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -336,10 +336,7 @@
       for (aura::Window* window : windows) {
         if (wm::GetWindowState(window)->IsMinimized())
           continue;
-
-        ScopedAnimationDisabler disable(window);
-        window->Hide();
-        wm::GetWindowState(window)->Minimize();
+        wm::HideAndMinimizeWithoutAnimation(window);
       }
     }
 
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 9902a09..5c14180 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -10,13 +10,13 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
+#include "ash/scoped_animation_disabler.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/widget_finder.h"
 #include "ash/wm/window_positioning_utils.h"
-#include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
 #include "ash/ws/window_service_owner.h"
@@ -30,6 +30,7 @@
 #include "ui/aura/window_targeter.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/dip_util.h"
+#include "ui/compositor/layer_tree_owner.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
@@ -38,6 +39,7 @@
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/easy_resize_window_targeter.h"
+#include "ui/wm/core/window_animations.h"
 #include "ui/wm/core/window_properties.h"
 #include "ui/wm/core/window_util.h"
 #include "ui/wm/public/activation_client.h"
@@ -310,5 +312,22 @@
   }
 }
 
+void HideAndMinimizeWithoutAnimation(aura::Window* window) {
+  // Disable the animations using |disable|. However, doing so will skip
+  // detaching and recreating layers that animating does which has memory
+  // implications, so use recreate layers to get the same effect. See
+  // crbug.com/924802.
+  ScopedAnimationDisabler disable(window);
+  window->Hide();
+  wm::GetWindowState(window)->Minimize();
+  std::unique_ptr<ui::LayerTreeOwner> owner = ::wm::RecreateLayers(window);
+}
+
+void HideWithoutAnimation(aura::Window* window) {
+  ScopedAnimationDisabler disable(window);
+  window->Hide();
+  std::unique_ptr<ui::LayerTreeOwner> owner = ::wm::RecreateLayers(window);
+}
+
 }  // namespace wm
 }  // namespace ash
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index 40f4607..1121290721 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -116,6 +116,11 @@
 ASH_EXPORT void RemoveTransientDescendants(
     std::vector<aura::Window*>* out_window_list);
 
+// Minimizes or hides |window| without any animations, in case users what it to
+// hide right away or apply their own animations.
+ASH_EXPORT void HideAndMinimizeWithoutAnimation(aura::Window* window);
+ASH_EXPORT void HideWithoutAnimation(aura::Window* window);
+
 }  // namespace wm
 }  // namespace ash
 
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index ac15cf6..087ef06 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -108,15 +108,12 @@
   void InitTrace(const _CONTEXT* context_record);
 #endif
 
-#if defined(OS_WIN) || defined(OS_ANDROID)
-  // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
-  // the sum of FramesToSkip and FramesToCapture must be less than 63,
-  // so set it to 62.
-  // Testing indicates that Android has issues with a larger value
-  // here, so leave Android at 62 also.
+#if defined(OS_ANDROID)
+  // TODO(https://crbug.com/925525): Testing indicates that Android has issues
+  // with a larger value here, so leave Android at 62.
   static constexpr int kMaxTraces = 62;
 #else
-  // For other architectures, use 250. This seems reasonable without
+  // For other platforms, use 250. This seems reasonable without
   // being huge.
   static constexpr int kMaxTraces = 250;
 #endif
diff --git a/base/files/file.h b/base/files/file.h
index 30f40532..cc157a4 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -264,6 +264,10 @@
   bool GetInfo(Info* info);
 
 #if !defined(OS_FUCHSIA)  // Fuchsia's POSIX API does not support file locking.
+  enum class LockMode {
+    kShared,
+    kExclusive,
+  };
 
   // Attempts to take an exclusive write lock on the file. Returns immediately
   // (i.e. does not wait for another process to unlock the file). If the lock
@@ -283,9 +287,9 @@
   // POSIX-specific semantics:
   //  * Locks are advisory only.
   //  * Within a process, locking the same file (by the same or new handle)
-  //    will succeed.
+  //    will succeed. The new lock replaces the old lock.
   //  * Closing any descriptor on a given file releases the lock.
-  Error Lock();
+  Error Lock(LockMode mode = LockMode::kExclusive);
 
   // Unlock a file previously locked.
   Error Unlock();
diff --git a/base/files/file_locking_unittest.cc b/base/files/file_locking_unittest.cc
index e158b7d..638d74dd 100644
--- a/base/files/file_locking_unittest.cc
+++ b/base/files/file_locking_unittest.cc
@@ -23,6 +23,10 @@
 // Flag for the parent to share a temp dir to the child.
 const char kTempDirFlag[] = "temp-dir";
 
+// Flags to control how the process locks the file.
+const char kFileLockShared[] = "file-lock-shared";
+const char kFileLockExclusive[] = "file-lock-exclusive";
+
 // Flags to control how the subprocess unlocks the file.
 const char kFileUnlock[] = "file-unlock";
 const char kCloseUnlock[] = "close-unlock";
@@ -89,11 +93,18 @@
   const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag);
   CHECK(base::DirectoryExists(temp_path));
 
+  const bool use_shared_lock = command_line->HasSwitch(kFileLockShared);
+  const bool use_exclusive_lock = command_line->HasSwitch(kFileLockExclusive);
+  CHECK_NE(use_shared_lock, use_exclusive_lock);
+
+  const File::LockMode mode =
+      use_exclusive_lock ? File::LockMode::kExclusive : File::LockMode::kShared;
+
   // Immediately lock the file.
   File file(temp_path.AppendASCII(kLockFile),
             File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
   CHECK(file.IsValid());
-  CHECK_EQ(File::FILE_OK, file.Lock());
+  CHECK_EQ(File::FILE_OK, file.Lock(mode));
   CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
 
   if (command_line->HasSwitch(kFileUnlock)) {
@@ -144,15 +155,24 @@
                                      TestTimeouts::action_timeout());
   }
 
-  // Start a child process set to use the specified unlock action, and wait for
-  // it to lock the file.
-  void StartChildAndSignalLock(const char* unlock_action) {
+  // Start a child process set to use the specified locking mode and unlock
+  // action, and wait for it to lock the file.
+  void StartChildAndSignalLock(File::LockMode lock_mode,
+                               const char* unlock_action) {
     // Create a temporary dir and spin up a ChildLockExit subprocess against it.
     const FilePath temp_path = temp_dir_.GetPath();
     base::CommandLine child_command_line(
         base::GetMultiProcessTestChildBaseCommandLine());
     child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
     child_command_line.AppendSwitch(unlock_action);
+    switch (lock_mode) {
+      case File::LockMode::kExclusive:
+        child_command_line.AppendSwitch(kFileLockExclusive);
+        break;
+      case File::LockMode::kShared:
+        child_command_line.AppendSwitch(kFileLockShared);
+        break;
+    }
     lock_child_ = base::SpawnMultiProcessTestChild(
         ChildMainString, child_command_line, base::LaunchOptions());
     ASSERT_TRUE(lock_child_.IsValid());
@@ -179,38 +199,89 @@
 };
 
 // Test that locks are released by Unlock().
-TEST_F(FileLockingTest, LockAndUnlock) {
-  StartChildAndSignalLock(kFileUnlock);
+TEST_F(FileLockingTest, LockAndUnlockExclusive) {
+  StartChildAndSignalLock(File::LockMode::kExclusive, kFileUnlock);
 
-  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
   ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
   ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
-  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+
+  ExitChildCleanly();
+}
+TEST_F(FileLockingTest, LockAndUnlockShared) {
+  StartChildAndSignalLock(File::LockMode::kShared, kFileUnlock);
+
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
+  ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
 
   ExitChildCleanly();
 }
 
 // Test that locks are released on Close().
-TEST_F(FileLockingTest, UnlockOnClose) {
-  StartChildAndSignalLock(kCloseUnlock);
+TEST_F(FileLockingTest, UnlockOnCloseExclusive) {
+  StartChildAndSignalLock(File::LockMode::kExclusive, kCloseUnlock);
 
-  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
   ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
   ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
-  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+
+  ExitChildCleanly();
+}
+TEST_F(FileLockingTest, UnlockOnCloseShared) {
+  StartChildAndSignalLock(File::LockMode::kShared, kCloseUnlock);
+
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
+  ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
 
   ExitChildCleanly();
 }
 
 // Test that locks are released on exit.
-TEST_F(FileLockingTest, UnlockOnExit) {
-  StartChildAndSignalLock(kExitUnlock);
+TEST_F(FileLockingTest, UnlockOnExitExclusive) {
+  StartChildAndSignalLock(File::LockMode::kExclusive, kExitUnlock);
 
-  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
   ExitChildCleanly();
-  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+}
+TEST_F(FileLockingTest, UnlockOnExitShared) {
+  StartChildAndSignalLock(File::LockMode::kShared, kExitUnlock);
+
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
+  ExitChildCleanly();
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
 }
 
@@ -223,10 +294,12 @@
 #endif
 TEST_F(FileLockingTest, MAYBE_UnlockOnTerminate) {
   // The child will wait for an exit which never arrives.
-  StartChildAndSignalLock(kExitUnlock);
+  StartChildAndSignalLock(File::LockMode::kExclusive, kExitUnlock);
 
-  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
   ASSERT_TRUE(TerminateMultiProcessTestChild(lock_child_, 0, true));
-  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
 }
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index 6df7a8e..3a909938 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -72,9 +72,22 @@
 }
 
 #if !defined(OS_FUCHSIA)
-File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
+short FcntlFlockType(base::Optional<File::LockMode> mode) {
+  if (!mode.has_value())
+    return F_UNLCK;
+  switch (mode.value()) {
+    case File::LockMode::kShared:
+      return F_RDLCK;
+    case File::LockMode::kExclusive:
+      return F_WRLCK;
+  }
+  NOTREACHED();
+}
+
+File::Error CallFcntlFlock(PlatformFile file,
+                           base::Optional<File::LockMode> mode) {
   struct flock lock;
-  lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
+  lock.l_type = FcntlFlockType(std::move(mode));
   lock.l_whence = SEEK_SET;
   lock.l_start = 0;
   lock.l_len = 0;  // Lock entire file.
@@ -103,7 +116,8 @@
   return 0;
 }
 
-File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
+File::Error CallFcntlFlock(PlatformFile file,
+                           base::Optional<File::LockMode> mode) {
   NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
   return File::FILE_ERROR_INVALID_OPERATION;
 }
@@ -374,14 +388,14 @@
 }
 
 #if !defined(OS_FUCHSIA)
-File::Error File::Lock() {
+File::Error File::Lock(File::LockMode mode) {
   SCOPED_FILE_TRACE("Lock");
-  return CallFcntlFlock(file_.get(), true);
+  return CallFcntlFlock(file_.get(), mode);
 }
 
 File::Error File::Unlock() {
   SCOPED_FILE_TRACE("Unlock");
-  return CallFcntlFlock(file_.get(), false);
+  return CallFcntlFlock(file_.get(), base::Optional<File::LockMode>());
 }
 #endif
 
diff --git a/base/files/file_win.cc b/base/files/file_win.cc
index 842ceacb..2662713 100644
--- a/base/files/file_win.cc
+++ b/base/files/file_win.cc
@@ -68,7 +68,7 @@
   LARGE_INTEGER offset_li;
   offset_li.QuadPart = offset;
 
-  OVERLAPPED overlapped = {0};
+  OVERLAPPED overlapped = {};
   overlapped.Offset = offset_li.LowPart;
   overlapped.OffsetHigh = offset_li.HighPart;
 
@@ -119,7 +119,7 @@
   LARGE_INTEGER offset_li;
   offset_li.QuadPart = offset;
 
-  OVERLAPPED overlapped = {0};
+  OVERLAPPED overlapped = {};
   overlapped.Offset = offset_li.LowPart;
   overlapped.OffsetHigh = offset_li.HighPart;
 
@@ -229,12 +229,31 @@
   return true;
 }
 
-File::Error File::Lock() {
+namespace {
+
+DWORD LockFileFlagsForMode(File::LockMode mode) {
+  DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
+  switch (mode) {
+    case File::LockMode::kShared:
+      return flags;
+    case File::LockMode::kExclusive:
+      return flags | LOCKFILE_EXCLUSIVE_LOCK;
+  }
+  NOTREACHED();
+}
+
+}  // namespace
+
+File::Error File::Lock(File::LockMode mode) {
   DCHECK(IsValid());
 
   SCOPED_FILE_TRACE("Lock");
 
-  BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
+  OVERLAPPED overlapped = {};
+  BOOL result =
+      LockFileEx(file_.Get(), LockFileFlagsForMode(mode), /*dwReserved=*/0,
+                 /*nNumberOfBytesToLockLow=*/MAXDWORD,
+                 /*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
   if (!result)
     return GetLastFileError();
   return FILE_OK;
@@ -245,7 +264,11 @@
 
   SCOPED_FILE_TRACE("Unlock");
 
-  BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
+  OVERLAPPED overlapped = {};
+  BOOL result =
+      UnlockFileEx(file_.Get(), /*dwReserved=*/0,
+                   /*nNumberOfBytesToLockLow=*/MAXDWORD,
+                   /*nNumberOfBytesToLockHigh=*/MAXDWORD, &overlapped);
   if (!result)
     return GetLastFileError();
   return FILE_OK;
diff --git a/base/mac/foundation_util.h b/base/mac/foundation_util.h
index 433642f5..e4b3b6a 100644
--- a/base/mac/foundation_util.h
+++ b/base/mac/foundation_util.h
@@ -386,6 +386,13 @@
 // Converts |str| to a FilePath. Returns an empty path if |str| is nil.
 BASE_EXPORT FilePath NSStringToFilePath(NSString* str);
 
+// Converts a non-null |path| to a CFURLRef. |path| must not be empty.
+//
+// This function only uses manually-owned resources, so it does not depend on an
+// NSAutoreleasePool being set up on the current thread.
+BASE_EXPORT base::ScopedCFTypeRef<CFURLRef> FilePathToCFURL(
+    const FilePath& path);
+
 #if defined(__OBJC__)
 // Converts |range| to an NSRange, returning the new range in |range_out|.
 // Returns true if conversion was successful, false if the values of |range|
diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm
index 5046eed..b14e40b 100644
--- a/base/mac/foundation_util.mm
+++ b/base/mac/foundation_util.mm
@@ -448,6 +448,25 @@
   return FilePath([str fileSystemRepresentation]);
 }
 
+base::ScopedCFTypeRef<CFURLRef> FilePathToCFURL(const FilePath& path) {
+  DCHECK(!path.empty());
+
+  // The function's docs promise that it does not require an NSAutoreleasePool.
+  // A straightforward way to accomplish this is to use *Create* functions,
+  // combined with base::ScopedCFTypeRef.
+  const std::string& path_string = path.value();
+  base::ScopedCFTypeRef<CFStringRef> path_cfstring(CFStringCreateWithBytes(
+      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(path_string.data()),
+      path_string.length(), kCFStringEncodingUTF8,
+      /*isExternalRepresentation=*/FALSE));
+  if (!path_cfstring)
+    return base::ScopedCFTypeRef<CFURLRef>();
+
+  return base::ScopedCFTypeRef<CFURLRef>(CFURLCreateWithFileSystemPath(
+      kCFAllocatorDefault, path_cfstring, kCFURLPOSIXPathStyle,
+      /*isDirectory=*/FALSE));
+}
+
 bool CFRangeToNSRange(CFRange range, NSRange* range_out) {
   if (base::IsValueInRangeForNumericType<decltype(range_out->location)>(
           range.location) &&
diff --git a/base/mac/foundation_util_unittest.mm b/base/mac/foundation_util_unittest.mm
index 5214354..7b52574 100644
--- a/base/mac/foundation_util_unittest.mm
+++ b/base/mac/foundation_util_unittest.mm
@@ -316,6 +316,11 @@
   EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b"));
 }
 
+TEST(FoundationUtilTest, FilePathToCFURL) {
+  EXPECT_NSEQ([NSURL fileURLWithPath:@"/a/b"],
+              base::mac::CFToNSCast(FilePathToCFURL(FilePath("/a/b"))));
+}
+
 TEST(FoundationUtilTest, CFRangeToNSRange) {
   NSRange range_out;
   EXPECT_TRUE(CFRangeToNSRange(CFRangeMake(10, 5), &range_out));
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index f596efb..2ff62ec5 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -63,7 +63,11 @@
 BASE_EXPORT void SwitchFullScreenModes(FullScreenMode from_mode,
                                        FullScreenMode to_mode);
 
-// Excludes the file given by |file_path| from being backed up by Time Machine.
+// Returns true if the file at |file_path| is excluded from Time Machine
+// backups.
+BASE_EXPORT bool GetFileBackupExclusion(const FilePath& file_path);
+
+// Excludes the file given by |file_path| from Time Machine backups.
 BASE_EXPORT bool SetFileBackupExclusion(const FilePath& file_path);
 
 // Checks if the current application is set as a Login Item, so it will launch
diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm
index e4177d3..0065197 100644
--- a/base/mac/mac_util.mm
+++ b/base/mac/mac_util.mm
@@ -212,18 +212,20 @@
   SetUIMode();
 }
 
-bool SetFileBackupExclusion(const FilePath& file_path) {
-  NSString* file_path_ns = base::mac::FilePathToNSString(file_path);
-  NSURL* file_url = [NSURL fileURLWithPath:file_path_ns];
+bool GetFileBackupExclusion(const FilePath& file_path) {
+  return CSBackupIsItemExcluded(FilePathToCFURL(file_path), nullptr);
+}
 
+bool SetFileBackupExclusion(const FilePath& file_path) {
   // When excludeByPath is true the application must be running with root
   // privileges (admin for 10.6 and earlier) but the URL does not have to
   // already exist. When excludeByPath is false the URL must already exist but
   // can be used in non-root (or admin as above) mode. We use false so that
   // non-root (or admin) users don't get their TimeMachine drive filled up with
   // unnecessary backups.
-  OSStatus os_err =
-      CSBackupSetItemExcluded(base::mac::NSToCFCast(file_url), TRUE, FALSE);
+  OSStatus os_err = CSBackupSetItemExcluded(FilePathToCFURL(file_path),
+                                            /*exclude=*/TRUE,
+                                            /*excludeByPath=*/FALSE);
   if (os_err != noErr) {
     OSSTATUS_DLOG(WARNING, os_err)
         << "Failed to set backup exclusion for file '"
diff --git a/base/mac/mac_util_unittest.mm b/base/mac/mac_util_unittest.mm
index 13229c62..c1585be2 100644
--- a/base/mac/mac_util_unittest.mm
+++ b/base/mac/mac_util_unittest.mm
@@ -95,8 +95,7 @@
   }
 }
 
-// http://crbug.com/425745
-TEST_F(MacUtilTest, DISABLED_TestExcludeFileFromBackups) {
+TEST_F(MacUtilTest, TestExcludeFileFromBackups) {
   // The file must already exist in order to set its exclusion property.
   ScopedTempDir temp_dir_;
   ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -105,16 +104,17 @@
   // Dump something real into the file.
   ASSERT_EQ(static_cast<int>(base::size(dummy_data)),
             WriteFile(dummy_file_path, dummy_data, base::size(dummy_data)));
-  NSString* fileURLString = base::mac::FilePathToNSString(dummy_file_path);
-  NSURL* fileURL = [NSURL URLWithString:fileURLString];
   // Initial state should be non-excluded.
-  EXPECT_FALSE(CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), NULL));
+  EXPECT_FALSE(GetFileBackupExclusion(dummy_file_path));
   // Exclude the file.
-  EXPECT_TRUE(SetFileBackupExclusion(dummy_file_path));
-  // SetFileBackupExclusion never excludes by path.
+  ASSERT_TRUE(SetFileBackupExclusion(dummy_file_path));
+  EXPECT_TRUE(GetFileBackupExclusion(dummy_file_path));
+
+  // Ensure that SetFileBackupExclusion never excludes by path.
+  base::ScopedCFTypeRef<CFURLRef> file_url =
+      base::mac::FilePathToCFURL(dummy_file_path);
   Boolean excluded_by_path = FALSE;
-  Boolean excluded =
-      CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), &excluded_by_path);
+  Boolean excluded = CSBackupIsItemExcluded(file_url, &excluded_by_path);
   EXPECT_TRUE(excluded);
   EXPECT_FALSE(excluded_by_path);
 }
diff --git a/base/threading/thread_local.h b/base/threading/thread_local.h
index 9841325f..f976205 100644
--- a/base/threading/thread_local.h
+++ b/base/threading/thread_local.h
@@ -97,10 +97,7 @@
   ~ThreadLocalOwnedPointer() {
     // Assume that this thread is the only one with potential state left. This
     // is verified in ~CheckedThreadLocalOwnedPointer().
-    // TODO: Consider making ThreadLocalStorage::Slot::Set() be side-effect free
-    // if the slot isn't initialized and nullptr is passed.
-    if (Get())
-      Set(nullptr);
+    Set(nullptr);
   }
 
   T* Get() const { return static_cast<T*>(slot_.Get()); }
diff --git a/base/threading/thread_local_internal.h b/base/threading/thread_local_internal.h
index 384cb6f6..6f7fdc9 100644
--- a/base/threading/thread_local_internal.h
+++ b/base/threading/thread_local_internal.h
@@ -27,8 +27,7 @@
   CheckedThreadLocalOwnedPointer() = default;
 
   ~CheckedThreadLocalOwnedPointer() {
-    if (Get())
-      Set(nullptr);
+    Set(nullptr);
 
     DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0)
         << "Memory leak: Must join all threads or release all associated "
diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc
index 21fd323..b4dec7e 100644
--- a/base/threading/thread_local_storage.cc
+++ b/base/threading/thread_local_storage.cc
@@ -378,8 +378,11 @@
       PlatformThreadLocalStorage::GetTLSValue(
           base::subtle::NoBarrier_Load(&g_native_tls_key)));
   DCHECK_NE(tls_data, kDestroyed);
-  if (!tls_data)
+  if (!tls_data) {
+    if (!value)
+      return;
     tls_data = ConstructTlsVector();
+  }
   DCHECK_NE(slot_, kInvalidSlotValue);
   DCHECK_LT(slot_, kThreadLocalStorageSize);
   tls_data[slot_].data = value;
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 68dc6a00..1d1435c 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1492,6 +1492,12 @@
         # TODO(thakis): https://crbug.com/617318
         # Currently goma can not handle case sensitiveness for windows well.
         cflags += [ "-Wno-nonportable-include-path" ]
+
+        if (target_cpu == "arm64") {
+          # Work around problem with SwiftShader for Windows ARM64
+          # TODO(Tom.Tan@microsoft.com): https://crbug.com/925595
+          cflags += [ "-Wno-defaulted-function-deleted" ]
+        }
       }
 
       if (current_toolchain == host_toolchain || !use_xcode_clang) {
diff --git a/build/config/mac/mac_sdk_overrides.gni b/build/config/mac/mac_sdk_overrides.gni
index b05bc6a..3632678 100644
--- a/build/config/mac/mac_sdk_overrides.gni
+++ b/build/config/mac/mac_sdk_overrides.gni
@@ -9,8 +9,14 @@
 declare_args() {
   # Minimum supported version of the Mac SDK.
   if (_sdk_min_from_env == "") {
-    mac_sdk_min = "10.13"
+    mac_sdk_min = "10.12"
   } else {
     mac_sdk_min = _sdk_min_from_env
   }
-}
\ No newline at end of file
+}
+
+# Always assert that mac_sdk_min is used on non-macOS platforms to prevent
+# unused args warnings.
+if (!is_mac) {
+  assert(mac_sdk_min == "10.12" || true)
+}
diff --git a/build/fuchsia/remote_cmd.py b/build/fuchsia/remote_cmd.py
index 5f54747..cabdf1631 100644
--- a/build/fuchsia/remote_cmd.py
+++ b/build/fuchsia/remote_cmd.py
@@ -6,6 +6,7 @@
 import os
 import subprocess
 import sys
+import threading
 
 _SSH = ['ssh']
 _SCP = ['scp', '-C']  # Use gzip compression.
@@ -43,11 +44,13 @@
   def _GetSshCommandLinePrefix(self):
     return _SSH + ['-F', self._config_path, self._host, '-p', str(self._port)]
 
-  def RunCommand(self, command, silent):
+  def RunCommand(self, command, silent, timeout_secs=None):
     """Executes an SSH command on the remote host and blocks until completion.
 
     command: A list of strings containing the command and its arguments.
     silent: If true, suppresses all output from 'ssh'.
+    timeout_secs: If set, limits the amount of time that |command| may run.
+                  Commands which exceed the timeout are killed.
 
     Returns the exit code from the remote command."""
 
@@ -55,9 +58,24 @@
     _SSH_LOGGER.debug('ssh exec: ' + ' '.join(ssh_command))
     if silent:
       devnull = open(os.devnull, 'w')
-      return subprocess.call(ssh_command, stderr=devnull, stdout=devnull)
+      process = subprocess.Popen(ssh_command, stderr=devnull, stdout=devnull)
     else:
-      return subprocess.call(ssh_command)
+      process = subprocess.Popen(ssh_command)
+
+    timeout_timer = None
+    if timeout_secs:
+      timeout_timer = threading.Timer(timeout_secs, process.kill)
+      timeout_timer.start()
+
+    process.wait()
+
+    if timeout_timer:
+      timeout_timer.cancel()
+
+    if process.returncode == -9:
+      raise Exception('Timeout when executing \"%s\".' % ' '.join(command))
+
+    return process.returncode
 
 
   def RunCommandPiped(self, command = None, ssh_args = None, **kwargs):
diff --git a/build/fuchsia/run_package.py b/build/fuchsia/run_package.py
index 8f5c7da..8c2b575 100644
--- a/build/fuchsia/run_package.py
+++ b/build/fuchsia/run_package.py
@@ -30,6 +30,10 @@
 # Amount of time to wait for the termination of the system log output thread.
 _JOIN_TIMEOUT_SECS = 5
 
+# Amount of time to wait for Amber to complete package installation, as a
+# mitigation against hangs due to amber/network-related failures.
+_INSTALL_TIMEOUT_SECS = 5 * 60
+
 
 def _AttachKernelLogReader(target):
   """Attaches a kernel log reader as a long-running SSH task."""
@@ -234,7 +238,8 @@
                    (install_package_name, package_version))
       return_code = target.RunCommand(['amber_ctl', 'get_up', '-n',
                                        install_package_name, '-v',
-                                       package_version])
+                                       package_version],
+                                       timeout_secs=_INSTALL_TIMEOUT_SECS)
       if return_code != 0:
         raise Exception('Error while installing %s.' % install_package_name)
 
diff --git a/build/fuchsia/target.py b/build/fuchsia/target.py
index 3d112e1..26797f6 100644
--- a/build/fuchsia/target.py
+++ b/build/fuchsia/target.py
@@ -83,13 +83,14 @@
     logging.debug('running (non-blocking) \'%s\'.' % ' '.join(command))
     return self.GetCommandRunner().RunCommandPiped(command, **kwargs)
 
-  def RunCommand(self, command, silent=False):
+  def RunCommand(self, command, silent=False, timeout_secs=None):
     """Executes a remote command and waits for it to finish executing.
 
     Returns the exit code of the command."""
 
     logging.debug('running \'%s\'.' % ' '.join(command))
-    return self.GetCommandRunner().RunCommand(command, silent)
+    return self.GetCommandRunner().RunCommand(command, silent,
+                                              timeout_secs=timeout_secs)
 
   def PutFile(self, source, dest, recursive=False):
     """Copies a file from the local filesystem to the target filesystem.
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index 9d393c67..9f9d274 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -25,12 +25,18 @@
 
 # This can be changed after running:
 #    mac_toolchain upload -xcode-path path/to/Xcode.app
-MAC_TOOLCHAIN_VERSION = '9E145'
+MAC_TOOLCHAIN_VERSION = '8E2002'
 
 # The toolchain will not be downloaded if the minimum OS version is not met.
-# 17 is the major version number for macOS 10.13.
-# 9E145 (Xcode 9.3) only runs on 10.13.2 and newer.
-MAC_MINIMUM_OS_VERSION = 17
+# 16 is the major version number for macOS 10.12.
+MAC_MINIMUM_OS_VERSION = 16
+
+# The toolchain will not be downloaded if the maximum OS version is exceeded.
+# 17 is the major version number for macOS 10.13. Xcode 8 does not run on macOS
+# 10.14.
+# TODO(https://crbug.com/780980): Once we build with 10.13 SDK, Xcode 9, we
+# should be able to remove this upper bound.
+MAC_MAXIMUM_OS_VERSION = 17
 
 MAC_TOOLCHAIN_INSTALLER = 'mac_toolchain'
 
@@ -48,7 +54,8 @@
 
 def PlatformMeetsHermeticXcodeRequirements():
   major_version = int(platform.release().split('.')[0])
-  return major_version >= MAC_MINIMUM_OS_VERSION
+  return (major_version >= MAC_MINIMUM_OS_VERSION and
+          major_version <= MAC_MAXIMUM_OS_VERSION)
 
 
 def _UseHermeticToolchain():
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 7980e4b..7e0328a 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -439,7 +439,7 @@
 }
 
 bool LayerTreeHost::IsUsingLayerLists() const {
-  return settings_.use_layer_lists;
+  return settings_.use_layer_lists && !force_use_property_tree_builder_;
 }
 
 void LayerTreeHost::CommitComplete() {
@@ -662,10 +662,11 @@
     property_trees_.clear();
     return false;
   }
+
   DCHECK(!root_layer()->parent());
   base::ElapsedTimer timer;
 
-  bool result = DoUpdateLayers(root_layer());
+  bool result = DoUpdateLayers();
   micro_benchmark_controller_.DidUpdateLayers();
 
   if (const char* client_name = GetClientNameForMetrics()) {
@@ -736,7 +737,7 @@
   gpu_rasterization_histogram_recorded_ = true;
 }
 
-bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) {
+bool LayerTreeHost::DoUpdateLayers() {
   TRACE_EVENT1("cc,benchmark", "LayerTreeHost::DoUpdateLayers",
                "source_frame_number", SourceFrameNumber());
 
@@ -750,13 +751,13 @@
   if (!IsUsingLayerLists()) {
     TRACE_EVENT0("cc", "LayerTreeHost::UpdateLayers::BuildPropertyTrees");
     Layer* root_scroll =
-        PropertyTreeBuilder::FindFirstScrollableLayer(root_layer);
+        PropertyTreeBuilder::FindFirstScrollableLayer(root_layer_.get());
     Layer* page_scale_layer = viewport_layers_.page_scale.get();
     if (!page_scale_layer && root_scroll)
       page_scale_layer = root_scroll->parent();
     gfx::Transform identity_transform;
     PropertyTreeBuilder::BuildPropertyTrees(
-        root_layer, page_scale_layer, inner_viewport_scroll_layer(),
+        root_layer_.get(), page_scale_layer, inner_viewport_scroll_layer(),
         outer_viewport_scroll_layer(), overscroll_elasticity_element_id(),
         elastic_overscroll_, page_scale_factor_, device_scale_factor_,
         gfx::Rect(device_viewport_size_), identity_transform, &property_trees_);
@@ -792,6 +793,11 @@
     DCHECK(property_trees_.clip_tree.Node(layer->clip_tree_index()));
     DCHECK(property_trees_.scroll_tree.Node(layer->scroll_tree_index()));
   }
+#else
+  // This is a quick sanity check for readiness of paint properties.
+  // TODO(crbug.com/913464): This is to help analysis of crashes of the bug.
+  // Remove this CHECK when we close the bug.
+  CHECK(property_trees_.effect_tree.Node(root_layer_->effect_tree_index()));
 #endif
 
   draw_property_utils::UpdatePropertyTrees(this, &property_trees_);
@@ -1047,9 +1053,20 @@
   content_has_non_aa_paint_ = false;
   gpu_rasterization_histogram_recorded_ = false;
 
+  force_use_property_tree_builder_ = false;
+
   SetNeedsFullTreeSync();
 }
 
+void LayerTreeHost::SetNonBlinkManagedRootLayer(
+    scoped_refptr<Layer> root_layer) {
+  SetRootLayer(std::move(root_layer));
+
+  DCHECK(root_layer_->children().empty());
+  if (IsUsingLayerLists() && root_layer_)
+    force_use_property_tree_builder_ = true;
+}
+
 LayerTreeHost::ViewportLayers::ViewportLayers() = default;
 
 LayerTreeHost::ViewportLayers::~ViewportLayers() = default;
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 3dad3b5..d98608d 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -309,6 +309,14 @@
   Layer* root_layer() { return root_layer_.get(); }
   const Layer* root_layer() const { return root_layer_.get(); }
 
+  // Sets the root layer which is not managed by blink, and we will initialize
+  // its paint properties using PropertyTreeBuilder. For ui::Compositor, because
+  // for now we always use PropertyTreeBulder, this function is equivalent to
+  // SetRootLayer().
+  // TODO(crbug.com/925855): This is temporary. Eventually we should let the
+  // caller inform blink about the layer and remove the function.
+  void SetNonBlinkManagedRootLayer(scoped_refptr<Layer> root_layer);
+
   // Viewport Layers are used to identify key layers to the compositor thread,
   // so that it can perform viewport-based scrolling independently, such as
   // for pinch-zoom or overscroll elasticity.
@@ -708,7 +716,7 @@
   void ApplyPageScaleDeltaFromImplSide(float page_scale_delta);
   void InitializeProxy(std::unique_ptr<Proxy> proxy);
 
-  bool DoUpdateLayers(Layer* root_layer);
+  bool DoUpdateLayers();
 
   void UpdateDeferMainFrameUpdateInternal();
 
@@ -843,6 +851,13 @@
   // for every layer during property tree building.
   bool has_copy_request_ = false;
 
+  // When settings_.use_layer_lists is true, paint properties are generated by
+  // blink and we don't use PropertyTreeBuilder, except that the root layer
+  // is set by SetNonBlinkManagedRootLayer().
+  // TODO(crbug.com/925855): Remove this field when removing
+  // SetNonBlinkManagedRootLayer().
+  bool force_use_property_tree_builder_ = false;
+
   MutatorHost* mutator_host_;
 
   std::vector<std::pair<PaintImage, base::OnceCallback<void(bool)>>>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 782b2fd..e9666238 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -13,8 +13,8 @@
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
-import org.chromium.chrome.browser.autofill_assistant.overlay.TouchEventFilterView;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.Snackbar;
@@ -25,23 +25,12 @@
  * The main coordinator for the Autofill Assistant, responsible for instantiating all other
  * sub-components and shutting down the Autofill Assistant.
  */
-class AssistantCoordinator implements TouchEventFilterView.Delegate {
+class AssistantCoordinator {
     interface Delegate {
         /**
          * Completely stop the Autofill Assistant.
          */
         void stop();
-
-        /**
-         * Asks for an update of the touchable area.
-         */
-        void updateTouchableArea();
-
-        /**
-         * Called when interaction within allowed touchable area was detected. The interaction
-         * could be any gesture.
-         */
-        void onUserInteractionInsideTouchableArea();
     }
 
     private static final String FEEDBACK_CATEGORY_TAG =
@@ -77,7 +66,8 @@
         mBottomBarCoordinator =
                 new AssistantBottomBarCoordinator(activity, webContents, mAssistantView, mModel);
         mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity);
-        mOverlayCoordinator = new AssistantOverlayCoordinator(activity, mAssistantView, this);
+        mOverlayCoordinator =
+                new AssistantOverlayCoordinator(activity, mAssistantView, mModel.getOverlayModel());
 
         showAssistantView();
     }
@@ -108,7 +98,7 @@
         mBottomBarCoordinator.expand();
 
         // Hide everything except header.
-        mOverlayCoordinator.setState(AssistantOverlayState.hidden());
+        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.hidden());
         mModel.getDetailsModel().clearDetails();
         mBottomBarCoordinator.getPaymentRequestCoordinator().setVisible(false);
         mModel.getCarouselModel().clearChips();
@@ -139,7 +129,7 @@
         mModel.getHeaderModel().set(AssistantHeaderModel.CLOSE_VISIBLE, false);
 
         // Show overlay to prevent user from interacting with the page during onboarding.
-        mOverlayCoordinator.setState(AssistantOverlayState.full());
+        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.full());
 
         AssistantOnboardingCoordinator.show(mActivity, mBottomBarCoordinator.getView())
                 .then(accepted -> {
@@ -153,7 +143,8 @@
                     mModel.getHeaderModel().set(AssistantHeaderModel.CLOSE_VISIBLE, true);
 
                     // Hide overlay.
-                    mOverlayCoordinator.setState(AssistantOverlayState.hidden());
+                    mModel.getOverlayModel().set(
+                            AssistantOverlayModel.STATE, AssistantOverlayState.hidden());
 
                     onAccept.run();
                 });
@@ -214,10 +205,6 @@
         mActivity.getSnackbarManager().showSnackbar(snackBar);
     }
 
-    private void dismissAndShowSnackbar(int message, @DropOutReason int reason) {
-        dismissAndShowSnackbar(mActivity.getString(message), reason);
-    }
-
     /**
      * Show the Chrome feedback form.
      */
@@ -227,24 +214,6 @@
                 FeedbackContext.buildContextString(mActivity, debugContext, 4));
     }
 
-    // Implementation of methods from {@link TouchEventFilterView.Delegate}.
-
-    @Override
-    public void onUnexpectedTaps() {
-        dismissAndShowSnackbar(
-                R.string.autofill_assistant_maybe_give_up, DropOutReason.OVERLAY_STOP);
-    }
-
-    @Override
-    public void updateTouchableArea() {
-        mDelegate.updateTouchableArea();
-    }
-
-    @Override
-    public void onUserInteractionInsideTouchableArea() {
-        mDelegate.onUserInteractionInsideTouchableArea();
-    }
-
     // Private methods.
 
     private void showAssistantView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
index 2648ae9..df5b97b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
@@ -9,17 +9,24 @@
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel;
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsModel;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 
 /**
  * State for the Autofill Assistant UI.
  */
 @JNINamespace("autofill_assistant")
 class AssistantModel {
+    private final AssistantOverlayModel mOverlayModel = new AssistantOverlayModel();
     private final AssistantHeaderModel mHeaderModel = new AssistantHeaderModel();
     private final AssistantDetailsModel mDetailsModel = new AssistantDetailsModel();
     private final AssistantCarouselModel mCarouselModel = new AssistantCarouselModel();
 
     @CalledByNative
+    public AssistantOverlayModel getOverlayModel() {
+        return mOverlayModel;
+    }
+
+    @CalledByNative
     public AssistantHeaderModel getHeaderModel() {
         return mHeaderModel;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 7c232fb..153c6ebc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
-import android.graphics.RectF;
 import android.support.annotation.Nullable;
 
 import org.chromium.base.annotations.CalledByNative;
@@ -12,7 +11,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
-import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
 import org.chromium.chrome.browser.autofill_assistant.payment.AutofillAssistantPaymentRequest.SelectedPaymentInformation;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -22,9 +20,6 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentOptions;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Bridge to native side autofill_assistant::UiControllerAndroid. It allows native side to control
  * Autofill Assistant related UIs and forward UI events to native side.
@@ -96,16 +91,6 @@
         safeNativeStop();
     }
 
-    @Override
-    public void updateTouchableArea() {
-        safeNativeUpdateTouchableArea();
-    }
-
-    @Override
-    public void onUserInteractionInsideTouchableArea() {
-        safeNativeOnUserInteractionInsideTouchableArea();
-    }
-
     /**
      * Native => Java methods.
      */
@@ -132,26 +117,6 @@
     }
 
     @CalledByNative
-    private void onHideOverlay() {
-        mCoordinator.getOverlayCoordinator().setState(AssistantOverlayState.hidden());
-    }
-
-    @CalledByNative
-    private void onShowOverlay() {
-        mCoordinator.getOverlayCoordinator().setState(AssistantOverlayState.full());
-    }
-
-    @CalledByNative
-    private void updateTouchableArea(boolean enabled, float[] coords) {
-        List<RectF> boxes = new ArrayList<>();
-        for (int i = 0; i < coords.length; i += 4) {
-            boxes.add(new RectF(/* left= */ coords[i], /* top= */ coords[i + 1],
-                    /* right= */ coords[i + 2], /* bottom= */ coords[i + 3]));
-        }
-        mCoordinator.getOverlayCoordinator().setState(AssistantOverlayState.partial(boxes));
-    }
-
-    @CalledByNative
     private void onShutdown(@DropOutReason int reason) {
         mCoordinator.shutdownImmediately(reason);
     }
@@ -225,17 +190,6 @@
     }
     private native void nativeStop(long nativeUiControllerAndroid);
 
-    void safeNativeUpdateTouchableArea() {
-        if (mNativeUiController != 0) nativeUpdateTouchableArea(mNativeUiController);
-    }
-    private native void nativeUpdateTouchableArea(long nativeUiControllerAndroid);
-
-    void safeNativeOnUserInteractionInsideTouchableArea() {
-        if (mNativeUiController != 0)
-            nativeOnUserInteractionInsideTouchableArea(mNativeUiController);
-    }
-    private native void nativeOnUserInteractionInsideTouchableArea(long nativeUiControllerAndroid);
-
     void safeNativeOnGetPaymentInformation(boolean succeed,
             @Nullable PersonalDataManager.CreditCard card,
             @Nullable PersonalDataManager.AutofillProfile address, @Nullable String payerName,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
index 35246d4c..a65d624 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
@@ -7,7 +7,6 @@
 import android.view.View;
 
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.autofill_assistant.overlay.TouchEventFilterView.Delegate;
 
 /**
  * Coordinator responsible for showing a full or partial overlay on top of the web page currently
@@ -16,17 +15,26 @@
 public class AssistantOverlayCoordinator {
     private final ChromeActivity mActivity;
     private final TouchEventFilterView mTouchEventFilter;
-    private final Delegate mTouchEventFilterDelegate;
 
     public AssistantOverlayCoordinator(
-            ChromeActivity activity, View assistantView, Delegate touchEventFilterDelegate) {
+            ChromeActivity activity, View assistantView, AssistantOverlayModel model) {
         mActivity = activity;
         mTouchEventFilter = assistantView.findViewById(
                 org.chromium.chrome.autofill_assistant.R.id.touch_event_filter);
-        mTouchEventFilterDelegate = touchEventFilterDelegate;
 
-        mTouchEventFilter.init(mTouchEventFilterDelegate, mActivity.getFullscreenManager(),
+        mTouchEventFilter.init(mActivity.getFullscreenManager(),
                 mActivity.getActivityTab().getWebContents(), mActivity.getCompositorViewHolder());
+
+        // Listen for changes in the state.
+        // TODO(crbug.com/806868): Bind model to view through a ViewBinder instead.
+        model.addObserver((source, propertyKey) -> {
+            if (AssistantOverlayModel.STATE == propertyKey) {
+                AssistantOverlayState newState = model.get(AssistantOverlayModel.STATE);
+                setState(newState != null ? newState : AssistantOverlayState.hidden());
+            } else if (AssistantOverlayModel.DELEGATE == propertyKey) {
+                mTouchEventFilter.setDelegate(model.get(AssistantOverlayModel.DELEGATE));
+            }
+        });
     }
 
     /**
@@ -44,7 +52,7 @@
     /**
      * Set the overlay state.
      */
-    public void setState(AssistantOverlayState state) {
+    private void setState(AssistantOverlayState state) {
         if (state.isFull() && !mActivity.isViewObscuringAllTabs()) {
             mActivity.addViewObscuringAllTabs(mTouchEventFilter);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java
new file mode 100644
index 0000000..24f613f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.overlay;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/** Delegate for the overlay. */
+@JNINamespace("autofill_assistant")
+class AssistantOverlayDelegate {
+    private long mNativeAssistantOverlayDelegate;
+
+    @CalledByNative
+    private static AssistantOverlayDelegate create(long nativeAssistantOverlayDelegate) {
+        return new AssistantOverlayDelegate(nativeAssistantOverlayDelegate);
+    }
+
+    private AssistantOverlayDelegate(long nativeAssistantOverlayDelegate) {
+        mNativeAssistantOverlayDelegate = nativeAssistantOverlayDelegate;
+    }
+
+    /** Called after a certain number of unexpected taps. */
+    void onUnexpectedTaps() {
+        if (mNativeAssistantOverlayDelegate != 0) {
+            nativeOnUnexpectedTaps(mNativeAssistantOverlayDelegate);
+        }
+    }
+
+    /** Asks for an update of the touchable area. */
+    void updateTouchableArea() {
+        if (mNativeAssistantOverlayDelegate != 0) {
+            nativeUpdateTouchableArea(mNativeAssistantOverlayDelegate);
+        }
+    }
+
+    /**
+     * Called when interaction within allowed touchable area was detected. The interaction
+     * could be any gesture.
+     */
+    void onUserInteractionInsideTouchableArea() {
+        if (mNativeAssistantOverlayDelegate != 0) {
+            nativeOnUserInteractionInsideTouchableArea(mNativeAssistantOverlayDelegate);
+        }
+    }
+
+    @CalledByNative
+    private void clearNativePtr() {
+        mNativeAssistantOverlayDelegate = 0;
+    }
+
+    private native void nativeOnUnexpectedTaps(long nativeAssistantOverlayDelegate);
+    private native void nativeUpdateTouchableArea(long nativeAssistantOverlayDelegate);
+    private native void nativeOnUserInteractionInsideTouchableArea(
+            long nativeAssistantOverlayDelegate);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
new file mode 100644
index 0000000..a70c5b8
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.overlay;
+
+import android.graphics.RectF;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * State for the header of the Autofill Assistant.
+ */
+@JNINamespace("autofill_assistant")
+public class AssistantOverlayModel extends PropertyModel {
+    public static final WritableObjectPropertyKey<AssistantOverlayState> STATE =
+            new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<AssistantOverlayDelegate> DELEGATE =
+            new WritableObjectPropertyKey<>();
+
+    public AssistantOverlayModel() {
+        super(STATE, DELEGATE);
+    }
+
+    @CalledByNative
+    private void setHidden() {
+        set(STATE, AssistantOverlayState.hidden());
+    }
+
+    @CalledByNative
+    private void setFull() {
+        set(STATE, AssistantOverlayState.full());
+    }
+
+    @CalledByNative
+    private void setPartial(float[] coords) {
+        List<RectF> boxes = new ArrayList<>();
+        for (int i = 0; i < coords.length; i += 4) {
+            boxes.add(new RectF(/* left= */ coords[i], /* top= */ coords[i + 1],
+                    /* right= */ coords[i + 2], /* bottom= */ coords[i + 3]));
+        }
+        set(STATE, AssistantOverlayState.partial(boxes));
+    }
+
+    @CalledByNative
+    private void setDelegate(AssistantOverlayDelegate delegate) {
+        set(DELEGATE, delegate);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java
index affce18..3cabaea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java
@@ -72,8 +72,7 @@
     private static class Partial extends AssistantOverlayState {
         private final List<RectF> mBoxes;
 
-        private Partial(List<RectF> boxes /*, float[] coords*/) {
-            // this.coords = coords;
+        private Partial(List<RectF> boxes) {
             mBoxes = boxes;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
index 012c49c3..4ada116 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
@@ -53,20 +53,6 @@
  */
 public class TouchEventFilterView
         extends View implements ChromeFullscreenManager.FullscreenListener, GestureStateListener {
-    /** A client of this view. */
-    public interface Delegate {
-        /** Called after a certain number of unexpected taps. */
-        void onUnexpectedTaps();
-
-        /** Asks for an update of the touchable area. */
-        void updateTouchableArea();
-
-        /**
-         * Called when interaction within allowed touchable area was detected. The interaction
-         * could be any gesture.
-         */
-        void onUserInteractionInsideTouchableArea();
-    }
 
     /**
      * Complain after there's been {@link TAP_TRACKING_COUNT} taps within
@@ -92,7 +78,7 @@
     /** The current gesture is being forwarded to the content view. */
     private static final int FORWARDING_GESTURE_MODE = 2;
 
-    private Delegate mDelegate;
+    private AssistantOverlayDelegate mDelegate;
     private ChromeFullscreenManager mFullscreenManager;
     private GestureListenerManager mGestureListenerManager;
     private View mCompositorView;
@@ -224,9 +210,8 @@
     }
 
     /** Initializes dependencies. */
-    public void init(Delegate delegate, ChromeFullscreenManager fullscreenManager,
-            WebContents webContents, View compositorView) {
-        mDelegate = delegate;
+    public void init(ChromeFullscreenManager fullscreenManager, WebContents webContents,
+            View compositorView) {
         mFullscreenManager = fullscreenManager;
         mFullscreenManager.addListener(this);
         mGestureListenerManager = GestureListenerManager.fromWebContents(webContents);
@@ -250,6 +235,13 @@
     }
 
     /**
+     * Set this view delegate.
+     */
+    public void setDelegate(AssistantOverlayDelegate delegate) {
+        mDelegate = delegate;
+    }
+
+    /**
      * Set the current state of the overlay.
      */
     public void setState(AssistantOverlayState newState) {
@@ -347,7 +339,9 @@
                 resetCurrentGesture();
 
                 if (shouldLetEventThrough(event)) {
-                    mDelegate.onUserInteractionInsideTouchableArea();
+                    if (mDelegate != null) {
+                        mDelegate.onUserInteractionInsideTouchableArea();
+                    }
                     // This is the last we'll hear of this gesture unless it turns multi-touch. No
                     // need to track or forward it.
                     return false;
@@ -546,12 +540,12 @@
         if (!mBrowserScrolling) {
             // onScrollOffsetOrExtentChanged will be called alone, without onScrollStarted during a
             // Javascript-initiated scroll.
-            mDelegate.updateTouchableArea();
+            askForTouchableAreaUpdate();
             return;
         }
         mBrowserScrollOffsetY = scrollOffsetY - mInitialBrowserScrollOffsetY;
         invalidate();
-        mDelegate.updateTouchableArea();
+        askForTouchableAreaUpdate();
     }
 
     /** Called at the end of a scroll gesture triggered by the browser. */
@@ -564,7 +558,7 @@
         mBrowserScrollOffsetY = 0;
         mBrowserScrolling = false;
         invalidate();
-        mDelegate.updateTouchableArea();
+        askForTouchableAreaUpdate();
     }
 
     /** Considers whether to let the client know about unexpected taps. */
@@ -583,6 +577,12 @@
         }
     }
 
+    private void askForTouchableAreaUpdate() {
+        if (mDelegate != null) {
+            mDelegate.updateTouchableArea();
+        }
+    }
+
     private boolean isInTouchableArea(float x, float y) {
         for (RectF rect : mCurrentState.boxes()) {
             if (rect.contains(x, y, x, y)) {
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c987f13..58df017 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -160,6 +160,8 @@
   "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index ef76aa4..7726e16 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -35,6 +35,7 @@
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetails;
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsModel;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
@@ -139,8 +140,8 @@
         // Show overlay.
         ThreadUtils.runOnUiThreadBlocking(
                 ()
-                        -> assistantCoordinator.getOverlayCoordinator().setState(
-                                AssistantOverlayState.full()));
+                        -> assistantCoordinator.getModel().getOverlayModel().set(
+                                AssistantOverlayModel.STATE, AssistantOverlayState.full()));
         View overlay = bottomSheet.findViewById(R.id.touch_event_filter);
         Assert.assertTrue(overlay.isShown());
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3706703..acb93ecb 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8892,6 +8892,23 @@
       Unknown device [<ph name="VENDOR_ID">$1<ex>123</ex></ph>:<ph name="PRODUCT_ID">$2<ex>123</ex></ph>]
     </message>
 
+    <!-- Device descriptions for devices defined by policy with wildcards. -->
+    <message name="IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_NAME" desc="String describing a device by its vendor name when only the numeric product ID is available.">
+      Unknown product <ph name="PRODUCT_ID">$1<ex>123</ex></ph> from <ph name="VENDOR_NAME">$2<ex>Google</ex></ph>
+    </message>
+    <message name="IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_ID" desc="String describing a device when only the numeric product ID and vendor ID are available.">
+      Unknown product <ph name="PRODUCT_ID">$1<ex>123</ex></ph> from vendor <ph name="VENDOR_ID">$2<ex>123</ex></ph>
+    </message>
+    <message name="IDS_DEVICE_DESCRIPTION_FOR_VENDOR_ID" desc="String describing devices from a vendor by ID.">
+      Devices from vendor <ph name="VENDOR_ID">$1<ex>123</ex></ph>
+    </message>
+    <message name="IDS_DEVICE_DESCRIPTION_FOR_VENDOR_NAME" desc="String describing devices from a vendor.">
+      Devices from <ph name="VENDOR_NAME">$1<ex>Google</ex></ph>
+    </message>
+    <message name="IDS_DEVICE_DESCRIPTION_FOR_ANY_VENDOR" desc="String describing devices from any vendor.">
+      Devices from any vendor
+    </message>
+
     <!-- Serial port chooser -->
     <if expr="not is_android">
       <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a website.">
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_ANY_VENDOR.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_ANY_VENDOR.png.sha1
new file mode 100644
index 0000000..f76ff09
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_ANY_VENDOR.png.sha1
@@ -0,0 +1 @@
+8f45376c9753b56bb11780fb849bf868993738d0
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_ID.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_ID.png.sha1
new file mode 100644
index 0000000..e775d61
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_ID.png.sha1
@@ -0,0 +1 @@
+581d54e4a40ad41e17d42b0cb380fdc0e473da81
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_NAME.png.sha1
new file mode 100644
index 0000000..7a5fbd0
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_NAME.png.sha1
@@ -0,0 +1 @@
+1f7e93405740ac03d1feeb745d0d23650aace397
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_ID.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_ID.png.sha1
new file mode 100644
index 0000000..b1d8d2d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_ID.png.sha1
@@ -0,0 +1 @@
+0855be7521c7c705819bab0150ef6eaa66b9923f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_NAME.png.sha1
new file mode 100644
index 0000000..01cf736
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEVICE_DESCRIPTION_FOR_VENDOR_NAME.png.sha1
@@ -0,0 +1 @@
+7ce39d3a0dc6ed75361b89132d92a16c8b11ea22
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index b722dc9..bc57528 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2739,9 +2739,6 @@
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_RETRAIN" desc="Button label for retrain voice model.">
     Retrain
   </message>
-  <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_DELETE" desc="Button label for delete voice model.">
-    Delete
-  </message>
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_NOTIFICATION" desc="Title for a toggle that lets the assistant show you notifications.">
     Notifications
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 95b0c65..c0710bf 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1048,6 +1048,12 @@
     "performance_monitor/process_metrics_history.h",
     "performance_monitor/process_monitor.cc",
     "performance_monitor/process_monitor.h",
+    "performance_monitor/system_monitor.cc",
+    "performance_monitor/system_monitor.h",
+    "performance_monitor/system_monitor_helper_posix.cc",
+    "performance_monitor/system_monitor_helper_posix.h",
+    "performance_monitor/system_monitor_helper_win.cc",
+    "performance_monitor/system_monitor_helper_win.h",
     "permissions/chooser_context_base.cc",
     "permissions/chooser_context_base.h",
     "permissions/permission_context_base.cc",
@@ -2038,6 +2044,8 @@
       "android/autofill_assistant/assistant_carousel_delegate.h",
       "android/autofill_assistant/assistant_header_delegate.cc",
       "android/autofill_assistant/assistant_header_delegate.h",
+      "android/autofill_assistant/assistant_overlay_delegate.cc",
+      "android/autofill_assistant/assistant_overlay_delegate.h",
       "android/autofill_assistant/client_android.cc",
       "android/autofill_assistant/client_android.h",
       "android/autofill_assistant/ui_controller_android.cc",
@@ -3283,7 +3291,6 @@
       "upgrade_detector/upgrade_detector_chromeos.h",
     ]
     deps += [
-      "//ash",
       "//ash/components/quick_launch/public/mojom:constants",
       "//ash/public/cpp",
       "//chrome/browser/chromeos",
@@ -4787,6 +4794,8 @@
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsModel.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java",
+      "../android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java",
+      "../android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
       "../android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java",
       "../android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java",
       "../android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8a9c7d7..4556bda 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1486,13 +1486,6 @@
      SINGLE_VALUE_TYPE_AND_VALUE(switches::kAllowNaClSocketAPI, "*")},
 #endif  // ENABLE_PLUGINS
 #if defined(OS_CHROMEOS)
-    {"ash-enable-docked-magnifier",
-     flag_descriptions::kEnableDockedMagnifierName,
-     flag_descriptions::kEnableDockedMagnifierDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kDockedMagnifier)},
-    {"ash-enable-night-light", flag_descriptions::kEnableNightLightName,
-     flag_descriptions::kEnableNightLightDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kNightLight)},
     {"ash-enable-notification-scroll-bar",
      flag_descriptions::kEnableNotificationScrollBarName,
      flag_descriptions::kEnableNotificationScrollBarDescription, kOsCrOS,
@@ -3623,12 +3616,6 @@
                                     "TabSwitcherOnReturn")},
 #endif
 
-#if defined(OS_CHROMEOS)
-    {"enable-apps-grid-gap", flag_descriptions::kEnableAppsGridGapFeatureName,
-     flag_descriptions::kEnableAppsGridGapFeatureDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(app_list_features::kEnableAppsGridGapFeature)},
-#endif  // OS_CHROMEOS
-
     {"enable-layered-api", flag_descriptions::kLayeredAPIName,
      flag_descriptions::kLayeredAPIDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kLayeredAPI)},
diff --git a/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.cc
new file mode 100644
index 0000000..f0ddfbb4
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.cc
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h"
+
+#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
+#include "jni/AssistantOverlayDelegate_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace autofill_assistant {
+
+AssistantOverlayDelegate::AssistantOverlayDelegate(
+    UiControllerAndroid* ui_controller)
+    : ui_controller_(ui_controller) {
+  java_assistant_overlay_delegate_ = Java_AssistantOverlayDelegate_create(
+      AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
+}
+
+AssistantOverlayDelegate::~AssistantOverlayDelegate() {
+  Java_AssistantOverlayDelegate_clearNativePtr(
+      AttachCurrentThread(), java_assistant_overlay_delegate_);
+}
+
+void AssistantOverlayDelegate::OnUnexpectedTaps(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  ui_controller_->OnUnexpectedTaps();
+}
+
+void AssistantOverlayDelegate::UpdateTouchableArea(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  ui_controller_->UpdateTouchableArea();
+}
+
+void AssistantOverlayDelegate::OnUserInteractionInsideTouchableArea(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  ui_controller_->OnUserInteractionInsideTouchableArea();
+}
+
+base::android::ScopedJavaGlobalRef<jobject>
+AssistantOverlayDelegate::GetJavaObject() {
+  return java_assistant_overlay_delegate_;
+}
+
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h b/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h
new file mode 100644
index 0000000..8b5ede9
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_OVERLAY_DELEGATE_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_OVERLAY_DELEGATE_H_
+
+#include "base/android/scoped_java_ref.h"
+
+namespace autofill_assistant {
+class UiControllerAndroid;
+// Delegate class for the assistant overlay.
+class AssistantOverlayDelegate {
+ public:
+  explicit AssistantOverlayDelegate(UiControllerAndroid* ui_controller);
+  ~AssistantOverlayDelegate();
+
+  void OnUnexpectedTaps(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& jcaller);
+
+  void UpdateTouchableArea(JNIEnv* env,
+                           const base::android::JavaParamRef<jobject>& jcaller);
+
+  void OnUserInteractionInsideTouchableArea(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller);
+
+  base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
+
+ private:
+  UiControllerAndroid* ui_controller_;
+
+  // Java-side AssistantOverlayDelegate object.
+  base::android::ScopedJavaGlobalRef<jobject> java_assistant_overlay_delegate_;
+};
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_OVERLAY_DELEGATE_H_
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 64694cb..9448ca8 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -39,6 +39,7 @@
 #include "jni/AssistantDetails_jni.h"
 #include "jni/AssistantHeaderModel_jni.h"
 #include "jni/AssistantModel_jni.h"
+#include "jni/AssistantOverlayModel_jni.h"
 #include "jni/AutofillAssistantUiController_jni.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -54,6 +55,7 @@
                                          UiDelegate* ui_delegate)
     : client_(client),
       ui_delegate_(ui_delegate),
+      overlay_delegate_(this),
       header_delegate_(this),
       carousel_delegate_(this),
       weak_ptr_factory_(this) {
@@ -66,6 +68,10 @@
           env, web_contents->GetJavaWebContents(),
           reinterpret_cast<intptr_t>(this));
 
+  // Register overlay_delegate_ as delegate for the overlay.
+  Java_AssistantOverlayModel_setDelegate(env, GetOverlayModel(),
+                                         overlay_delegate_.GetJavaObject());
+
   // Register header_delegate_ as delegate for clicks on header buttons.
   Java_AssistantHeaderModel_setDelegate(env, GetHeaderModel(),
                                         header_delegate_.GetJavaObject());
@@ -158,16 +164,6 @@
   // TODO(crbug.com/806868): Remove calls to this function.
 }
 
-void UiControllerAndroid::ShowOverlay() {
-  Java_AutofillAssistantUiController_onShowOverlay(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_);
-}
-
-void UiControllerAndroid::HideOverlay() {
-  Java_AutofillAssistantUiController_onHideOverlay(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_);
-}
-
 void UiControllerAndroid::AllowShowingSoftKeyboard(bool enabled) {
   Java_AutofillAssistantUiController_onAllowShowingSoftKeyboard(
       AttachCurrentThread(), java_autofill_assistant_ui_controller_, enabled);
@@ -256,6 +252,53 @@
   }
 }
 
+// Overlay related methods.
+
+base::android::ScopedJavaLocalRef<jobject>
+UiControllerAndroid::GetOverlayModel() {
+  return Java_AssistantModel_getOverlayModel(AttachCurrentThread(), GetModel());
+}
+
+void UiControllerAndroid::ShowOverlay() {
+  Java_AssistantOverlayModel_setFull(AttachCurrentThread(), GetOverlayModel());
+}
+
+void UiControllerAndroid::HideOverlay() {
+  Java_AssistantOverlayModel_setHidden(AttachCurrentThread(),
+                                       GetOverlayModel());
+}
+
+void UiControllerAndroid::UpdateTouchableArea(bool enabled,
+                                              const std::vector<RectF>& areas) {
+  JNIEnv* env = AttachCurrentThread();
+  std::vector<float> flattened;
+  for (const auto& rect : areas) {
+    flattened.emplace_back(rect.left);
+    flattened.emplace_back(rect.top);
+    flattened.emplace_back(rect.right);
+    flattened.emplace_back(rect.bottom);
+  }
+  Java_AssistantOverlayModel_setPartial(
+      env, GetOverlayModel(), base::android::ToJavaFloatArray(env, flattened));
+}
+
+void UiControllerAndroid::OnUnexpectedTaps() {
+  JNIEnv* env = AttachCurrentThread();
+  Java_AutofillAssistantUiController_dismissAndShowSnackbar(
+      env, java_autofill_assistant_ui_controller_,
+      base::android::ConvertUTF8ToJavaString(
+          env, l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_MAYBE_GIVE_UP)),
+      Metrics::OVERLAY_STOP);
+}
+
+void UiControllerAndroid::UpdateTouchableArea() {
+  ui_delegate_->UpdateTouchableArea();
+}
+
+void UiControllerAndroid::OnUserInteractionInsideTouchableArea() {
+  ui_delegate_->OnUserInteractionInsideTouchableArea();
+}
+
 // Other methods.
 
 void UiControllerAndroid::ShowOnboarding(
@@ -275,18 +318,6 @@
       AttachCurrentThread(), java_autofill_assistant_ui_controller_);
 }
 
-void UiControllerAndroid::UpdateTouchableArea(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) {
-  ui_delegate_->UpdateTouchableArea();
-}
-
-void UiControllerAndroid::OnUserInteractionInsideTouchableArea(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcallerj) {
-  ui_delegate_->OnUserInteractionInsideTouchableArea();
-}
-
 void UiControllerAndroid::OnGetPaymentInformation(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
@@ -399,19 +430,4 @@
     const base::android::JavaParamRef<jobject>& obj) {
   client_->Stop();
 }
-
-void UiControllerAndroid::UpdateTouchableArea(bool enabled,
-                                              const std::vector<RectF>& areas) {
-  JNIEnv* env = AttachCurrentThread();
-  std::vector<float> flattened;
-  for (const auto& rect : areas) {
-    flattened.emplace_back(rect.left);
-    flattened.emplace_back(rect.top);
-    flattened.emplace_back(rect.right);
-    flattened.emplace_back(rect.bottom);
-  }
-  Java_AutofillAssistantUiController_updateTouchableArea(
-      env, java_autofill_assistant_ui_controller_, enabled,
-      base::android::ToJavaFloatArray(env, flattened));
-}
 }  // namespace autofill_assistant.
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 15ca21e..d958cb9 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_header_delegate.h"
+#include "chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/metrics.h"
@@ -54,6 +55,11 @@
   void UpdateTouchableArea(bool enabled,
                            const std::vector<RectF>& areas) override;
 
+  // Called by AssistantOverlayDelegate:
+  void OnUnexpectedTaps();
+  void UpdateTouchableArea();
+  void OnUserInteractionInsideTouchableArea();
+
   // Called by AssistantHeaderDelegate:
   void OnFeedbackButtonClicked();
   void OnCloseButtonClicked();
@@ -63,11 +69,6 @@
 
   // Called by Java.
   void Stop(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
-  void UpdateTouchableArea(JNIEnv* env,
-                           const base::android::JavaParamRef<jobject>& obj);
-  void OnUserInteractionInsideTouchableArea(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller);
   void OnGetPaymentInformation(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
@@ -85,10 +86,12 @@
  private:
   Client* const client_;
   UiDelegate* const ui_delegate_;
+  AssistantOverlayDelegate overlay_delegate_;
   AssistantHeaderDelegate header_delegate_;
   AssistantCarouselDelegate carousel_delegate_;
 
   base::android::ScopedJavaLocalRef<jobject> GetModel();
+  base::android::ScopedJavaLocalRef<jobject> GetOverlayModel();
   base::android::ScopedJavaLocalRef<jobject> GetHeaderModel();
   base::android::ScopedJavaLocalRef<jobject> GetDetailsModel();
   base::android::ScopedJavaLocalRef<jobject> GetCarouselModel();
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 824a71c4..5695e30 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -1390,6 +1390,6 @@
 
 // Some storage backend use a different code path for full deletions and
 // partial deletions, so we need to test both.
-INSTANTIATE_TEST_CASE_P(/* no prefix */,
-                        BrowsingDataRemoverBrowserTestP,
-                        ::testing::Values(base::Time(), kLastHour));
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         BrowsingDataRemoverBrowserTestP,
+                         ::testing::Values(base::Time(), kLastHour));
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
index c31e75fe..e210861 100644
--- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
+++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_browsertest.cc
@@ -62,9 +62,9 @@
 };
 
 // Instantiate test for unified consent disabled & enabled.
-INSTANTIATE_TEST_CASE_P(,
-                        BrowsingDataCounterUtilsBrowserTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(,
+                         BrowsingDataCounterUtilsBrowserTest,
+                         ::testing::Bool());
 
 IN_PROC_BROWSER_TEST_P(BrowsingDataCounterUtilsBrowserTest,
                        ShouldShowCookieException) {
diff --git a/chrome/browser/chromeos/accessibility/magnification_manager.cc b/chrome/browser/chromeos/accessibility/magnification_manager.cc
index 8f4e741..dac25cd 100644
--- a/chrome/browser/chromeos/accessibility/magnification_manager.cc
+++ b/chrome/browser/chromeos/accessibility/magnification_manager.cc
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/magnifier/magnification_controller.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/shell.h"
@@ -66,7 +65,7 @@
 }
 
 bool MagnificationManager::IsDockedMagnifierEnabled() const {
-  return ash::features::IsDockedMagnifierEnabled() && profile_ &&
+  return profile_ &&
          profile_->GetPrefs()->GetBoolean(ash::prefs::kDockedMagnifierEnabled);
 }
 
@@ -112,12 +111,9 @@
                  content::NotificationService::AllSources());
 
   // Connect to ash's DockedMagnifierController interface.
-  if (ash::features::IsDockedMagnifierEnabled()) {
-    content::ServiceManagerConnection::GetForProcess()
-        ->GetConnector()
-        ->BindInterface(ash::mojom::kServiceName,
-                        &docked_magnifier_controller_);
-  }
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &docked_magnifier_controller_);
 }
 
 MagnificationManager::~MagnificationManager() {
diff --git a/chrome/browser/chromeos/accessibility/speech_monitor.cc b/chrome/browser/chromeos/accessibility/speech_monitor.cc
index 33dd1e3..0100433 100644
--- a/chrome/browser/chromeos/accessibility/speech_monitor.cc
+++ b/chrome/browser/chromeos/accessibility/speech_monitor.cc
@@ -78,7 +78,7 @@
     const content::UtteranceContinuousParameters& params) {
   content::TtsController::GetInstance()->OnTtsEvent(
       utterance_id, content::TTS_EVENT_END, static_cast<int>(utterance.size()),
-      std::string());
+      0, std::string());
   return true;
 }
 
diff --git a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
index f311448..51b8d17a 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
@@ -28,7 +28,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/account_manager/account_manager.h"
@@ -39,9 +39,10 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/webdata/token_web_data.h"
 #include "components/webdata/common/web_data_service_consumer.h"
+#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 namespace chromeos {
 
@@ -88,10 +89,10 @@
  public:
   AccountMigrationBaseStep(const std::string& id,
                            AccountManager* account_manager,
-                           AccountTrackerService* account_tracker_service)
+                           identity::IdentityManager* identity_manager)
       : AccountMigrationRunner::Step(id),
         account_manager_(account_manager),
-        account_tracker_service_(account_tracker_service),
+        identity_manager_(identity_manager),
         weak_factory_(this) {}
   ~AccountMigrationBaseStep() override = default;
 
@@ -116,12 +117,14 @@
       return;
     }
 
-    // |AccountTrackerService::SeedAccountInfo| must be called before
+    // |IdentityManager::LegacySeedAccountInfo| must be called before
     // |AccountManager::UpsertToken|. |AccountManager| observers will need to
     // translate |AccountManager::AccountKey| to other formats using
-    // |AccountTrackerService| and hence |AccountTrackerService| should be
-    // updated first.
-    account_tracker_service_->SeedAccountInfo(gaia_id, email);
+    // |IdentityManager| and hence |IdentityManager| should be updated first.
+    AccountInfo account_info;
+    account_info.email = email;
+    account_info.gaia = gaia_id;
+    identity_manager_->LegacySeedAccountInfo(account_info);
     account_manager_->UpsertToken(
         AccountManager::AccountKey{
             gaia_id, account_manager::AccountType::ACCOUNT_TYPE_GAIA},
@@ -154,7 +157,7 @@
   AccountManager* const account_manager_;
 
   // Non-owning pointer.
-  AccountTrackerService* const account_tracker_service_;
+  identity::IdentityManager* const identity_manager_;
 
   // A temporary cache of accounts in |AccountManager|, guaranteed to be
   // up-to-date when |StartMigration| is called.
@@ -171,11 +174,12 @@
  public:
   DeviceAccountMigration(AccountManager::AccountKey device_account,
                          AccountManager* account_manager,
+                         identity::IdentityManager* identity_manager,
                          AccountTrackerService* account_tracker_service,
                          scoped_refptr<TokenWebData> token_web_data)
       : AccountMigrationBaseStep(kDeviceAccountMigration,
                                  account_manager,
-                                 account_tracker_service),
+                                 identity_manager),
         account_mapper_util_(account_tracker_service),
         token_web_data_(token_web_data),
         device_account_(device_account) {}
@@ -268,39 +272,34 @@
 // to |AccountManager|. The objective is to migrate the account names only. We
 // cannot migrate any credentials (cookies).
 class ContentAreaAccountsMigration : public AccountMigrationBaseStep,
-                                     GaiaCookieManagerService::Observer {
+                                     identity::IdentityManager::Observer {
  public:
-  ContentAreaAccountsMigration(
-      AccountManager* account_manager,
-      AccountTrackerService* const account_tracker_service,
-      GaiaCookieManagerService* gaia_cookie_manager_service)
+  ContentAreaAccountsMigration(AccountManager* account_manager,
+                               identity::IdentityManager* identity_manager)
       : AccountMigrationBaseStep(kContentAreaAccountsMigration,
                                  account_manager,
-                                 account_tracker_service),
-        gaia_cookie_manager_service_(gaia_cookie_manager_service) {}
+                                 identity_manager),
+        identity_manager_(identity_manager) {}
   ~ContentAreaAccountsMigration() override {
-    gaia_cookie_manager_service_->RemoveObserver(this);
+    identity_manager_->RemoveObserver(this);
   }
 
  private:
   void StartMigration() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    std::vector<gaia::ListedAccount> signed_in_content_area_accounts;
-    std::vector<gaia::ListedAccount> signed_out_content_area_accounts;
-    gaia_cookie_manager_service_->AddObserver(this);
-    if (gaia_cookie_manager_service_->ListAccounts(
-            &signed_in_content_area_accounts,
-            &signed_out_content_area_accounts)) {
-      OnGaiaAccountsInCookieUpdated(
-          signed_in_content_area_accounts, signed_out_content_area_accounts,
+    identity_manager_->AddObserver(this);
+    identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
+        identity_manager_->GetAccountsInCookieJar();
+    if (accounts_in_cookie_jar_info.accounts_are_fresh) {
+      OnAccountsInCookieUpdated(
+          accounts_in_cookie_jar_info,
           GoogleServiceAuthError(GoogleServiceAuthError::NONE));
     }
   }
 
-  void OnGaiaAccountsInCookieUpdated(
-      const std::vector<gaia::ListedAccount>& signed_in_content_area_accounts,
-      const std::vector<gaia::ListedAccount>& signed_out_content_area_accounts,
+  void OnAccountsInCookieUpdated(
+      const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
       const GoogleServiceAuthError& error) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     // We should not have reached here without |OnGetAccounts| having been
@@ -308,10 +307,10 @@
     // Furthermore, Account Manager must have been populated with the Device
     // Account before this |Step| is run.
     DCHECK(!IsAccountManagerEmpty());
-    gaia_cookie_manager_service_->RemoveObserver(this);
+    identity_manager_->RemoveObserver(this);
 
-    MigrateAccounts(signed_in_content_area_accounts,
-                    signed_out_content_area_accounts);
+    MigrateAccounts(accounts_in_cookie_jar_info.signed_in_accounts,
+                    accounts_in_cookie_jar_info.signed_out_accounts);
 
     FinishWithSuccess();
   }
@@ -329,8 +328,8 @@
     }
   }
 
-  // A non-owning pointer to |GaiaCookieManagerService|.
-  GaiaCookieManagerService* const gaia_cookie_manager_service_;
+  // A non-owning pointer to |IdentityManager|.
+  identity::IdentityManager* const identity_manager_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -347,11 +346,11 @@
                              public arc::ArcSessionManager::Observer {
  public:
   ArcAccountsMigration(AccountManager* account_manager,
-                       AccountTrackerService* account_tracker_service,
+                       identity::IdentityManager* identity_manager,
                        arc::ArcAuthService* arc_auth_service)
       : AccountMigrationBaseStep(kArcAccountsMigration,
                                  account_manager,
-                                 account_tracker_service),
+                                 identity_manager),
         arc_auth_service_(arc_auth_service),
         weak_factory_(this) {}
   ~ArcAccountsMigration() override { Reset(); }
@@ -510,25 +509,23 @@
       g_browser_process->platform_part()->GetAccountManagerFactory();
   chromeos::AccountManager* account_manager =
       factory->GetAccountManager(profile_->GetPath().value());
-
-  AccountTrackerService* account_tracker_service =
-      AccountTrackerServiceFactory::GetForProfile(profile_);
+  identity::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile_);
 
   migration_runner_.AddStep(std::make_unique<DeviceAccountMigration>(
-      device_account, account_manager, account_tracker_service,
+      device_account, account_manager, identity_manager,
+      AccountTrackerServiceFactory::GetForProfile(profile_),
       WebDataServiceFactory::GetTokenWebDataForProfile(
           profile_, ServiceAccessType::EXPLICIT_ACCESS) /* token_web_data */));
   migration_runner_.AddStep(std::make_unique<ContentAreaAccountsMigration>(
-      account_manager, account_tracker_service,
-      GaiaCookieManagerServiceFactory::GetForProfile(
-          profile_) /* gaia_cookie_manager_service */));
+      account_manager, identity_manager));
 
   if (arc::IsArcProvisioned(profile_)) {
     // Add a migration step for ARC only if ARC has been provisioned. If ARC has
     // not been provisioned yet, there cannot be any accounts that need to be
     // migrated.
     migration_runner_.AddStep(std::make_unique<ArcAccountsMigration>(
-        account_manager, account_tracker_service,
+        account_manager, identity_manager,
         arc::ArcAuthService::GetForBrowserContext(
             profile_) /* arc_auth_service */));
   } else {
@@ -605,7 +602,7 @@
   // be re-enabled once migration is done.
   DependsOn(AccountReconcilorFactory::GetInstance());
   // For getting Chrome content area accounts.
-  DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
 }
 
 AccountManagerMigratorFactory::~AccountManagerMigratorFactory() = default;
diff --git a/chrome/browser/chromeos/arc/tts/arc_tts_service.cc b/chrome/browser/chromeos/arc/tts/arc_tts_service.cc
index 444bef5..284c1dc 100644
--- a/chrome/browser/chromeos/arc/tts/arc_tts_service.cc
+++ b/chrome/browser/chromeos/arc/tts/arc_tts_service.cc
@@ -87,7 +87,7 @@
       chrome_event_type = content::TTS_EVENT_ERROR;
       break;
   }
-  tts_controller_->OnTtsEvent(id, chrome_event_type, char_index, error_msg);
+  tts_controller_->OnTtsEvent(id, chrome_event_type, char_index, -1, error_msg);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/tts/arc_tts_service_unittest.cc b/chrome/browser/chromeos/arc/tts/arc_tts_service_unittest.cc
index c0e16cdc..966c4e1 100644
--- a/chrome/browser/chromeos/arc/tts/arc_tts_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/tts/arc_tts_service_unittest.cc
@@ -28,10 +28,12 @@
   void OnTtsEvent(int utterance_id,
                   content::TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override {
     last_utterance_id_ = utterance_id;
     last_event_type_ = event_type;
     last_char_index_ = char_index;
+    last_length_ = length;
     last_error_message_ = error_message;
   }
 
@@ -60,6 +62,7 @@
   int last_utterance_id_;
   content::TtsEventType last_event_type_;
   int last_char_index_;
+  int last_length_;
   std::string last_error_message_;
 
  private:
@@ -104,25 +107,25 @@
   tts_service()->OnTtsEvent(1, mojom::TtsEventType::START, 0, "");
   EXPECT_EQ(1, tts_controller()->last_utterance_id_);
   EXPECT_EQ(content::TTS_EVENT_START, tts_controller()->last_event_type_);
-  EXPECT_EQ(0, tts_controller()->last_char_index_);
+  EXPECT_EQ(-1, tts_controller()->last_length_);
   EXPECT_EQ("", tts_controller()->last_error_message_);
 
   tts_service()->OnTtsEvent(1, mojom::TtsEventType::END, 10, "");
   EXPECT_EQ(1, tts_controller()->last_utterance_id_);
   EXPECT_EQ(content::TTS_EVENT_END, tts_controller()->last_event_type_);
-  EXPECT_EQ(10, tts_controller()->last_char_index_);
+  EXPECT_EQ(-1, tts_controller()->last_length_);
   EXPECT_EQ("", tts_controller()->last_error_message_);
 
   tts_service()->OnTtsEvent(2, mojom::TtsEventType::INTERRUPTED, 0, "");
   EXPECT_EQ(2, tts_controller()->last_utterance_id_);
   EXPECT_EQ(content::TTS_EVENT_INTERRUPTED, tts_controller()->last_event_type_);
-  EXPECT_EQ(0, tts_controller()->last_char_index_);
+  EXPECT_EQ(-1, tts_controller()->last_length_);
   EXPECT_EQ("", tts_controller()->last_error_message_);
 
   tts_service()->OnTtsEvent(3, mojom::TtsEventType::ERROR, 0, "");
   EXPECT_EQ(3, tts_controller()->last_utterance_id_);
   EXPECT_EQ(content::TTS_EVENT_ERROR, tts_controller()->last_event_type_);
-  EXPECT_EQ(0, tts_controller()->last_char_index_);
+  EXPECT_EQ(-1, tts_controller()->last_length_);
   EXPECT_EQ("", tts_controller()->last_error_message_);
 }
 
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index d00ff627..4ac4e54 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -958,7 +958,6 @@
       configs += [ "//build/config/linux/dbus" ]
     }
     deps += [
-      "//ash",
       "//ash/public/cpp",
       "//chromeos/attestation",
       "//chromeos/components/proximity_auth",
diff --git a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api.cc b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api.cc
index 3500e10..dfba45b 100644
--- a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api.cc
+++ b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api.cc
@@ -100,16 +100,20 @@
   syncer::DeviceInfoSyncService* service =
       DeviceInfoSyncServiceFactory::GetForProfile(profile);
   if (!service) {
-    return std::unique_ptr<DeviceInfo>();
+    return nullptr;
   }
 
-  const LocalDeviceInfoProvider* local_device =
+  const LocalDeviceInfoProvider* local_device_info_provider =
       service->GetLocalDeviceInfoProvider();
-  DCHECK(local_device);
-  std::string guid = local_device->GetLocalSyncCacheGUID();
-  std::unique_ptr<DeviceInfo> device =
-      GetDeviceInfoForClientId(guid, extension_id, profile);
-  return device;
+  DCHECK(local_device_info_provider);
+  const DeviceInfo* local_device =
+      local_device_info_provider->GetLocalDeviceInfo();
+  if (!local_device)
+    return nullptr;
+
+  // TODO(karandeepb): Can't we just return a copy of |local_device|, without
+  // having to look it up by GUID?
+  return GetDeviceInfoForClientId(local_device->guid(), extension_id, profile);
 }
 
 ExtensionFunction::ResponseAction SignedInDevicesGetFunction::Run() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 4fffff4..dc664bc 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -159,16 +159,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "ash-enable-docked-magnifier",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
-    "name": "ash-enable-night-light",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "ash-enable-notification-expansion-animation",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b9b1c92..cfb4f59 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -559,10 +559,6 @@
     "Experimental system for using the Desktop PWA framework for running System"
     "Apps (e.g Settings, Discover).";
 
-const char kEnableDockedMagnifierName[] = "Docked Magnifier";
-const char kEnableDockedMagnifierDescription[] =
-    "Enables the Docked Magnifier (a.k.a. picture-in-picture magnifier).";
-
 const char kEnforceTLS13DowngradeName[] = "TLS 1.3 downgrade hardening";
 const char kEnforceTLS13DowngradeDescription[] =
     "This option enables the TLS 1.3 downgrade hardening mechanism. This "
@@ -691,11 +687,6 @@
 const char kEnableNetworkServiceInProcessDescription[] =
     "Runs the network service in the browser process.";
 
-const char kEnableNightLightName[] = "Enable Night Light";
-const char kEnableNightLightDescription[] =
-    "Enable the Night Light feature which controls the color temperature of "
-    "the screen.";
-
 const char kEnableNotificationScrollBarName[] =
     "Enable notification list scroll bar";
 const char kEnableNotificationScrollBarDescription[] =
@@ -3190,12 +3181,6 @@
     "Allow launcher search to access data available through Firebase App "
     "Indexing";
 
-const char kEnableAppsGridGapFeatureName[] =
-    "Enable apps grid gap feature in launcher.";
-const char kEnableAppsGridGapFeatureDescription[] =
-    "Enables gaps at the end of each page and enables dragging an item to an "
-    "empty page in launcher.";
-
 const char kEnableArcUnifiedAudioFocusName[] =
     "Enable unified audio focus on ARC";
 const char kEnableArcUnifiedAudioFocusDescription[] =
@@ -3706,8 +3691,7 @@
 
 #if defined(OS_CHROMEOS)
 const char kPdfAnnotations[] = "PDF Annotations";
-const char kPdfAnnotationsDescription[] =
-    "Enable annotating PDF documents.";
+const char kPdfAnnotationsDescription[] = "Enable annotating PDF documents.";
 #endif  // defined(OS_CHROMEOS)
 
 const char kPdfFormSaveName[] = "Save PDF Forms";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a80b3e4..d03fab1 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -370,9 +370,6 @@
 extern const char kEnableSystemWebAppsName[];
 extern const char kEnableSystemWebAppsDescription[];
 
-extern const char kEnableDockedMagnifierName[];
-extern const char kEnableDockedMagnifierDescription[];
-
 extern const char kEnforceTLS13DowngradeName[];
 extern const char kEnforceTLS13DowngradeDescription[];
 
@@ -445,9 +442,6 @@
 extern const char kEnableNetworkServiceInProcessName[];
 extern const char kEnableNetworkServiceInProcessDescription[];
 
-extern const char kEnableNightLightName[];
-extern const char kEnableNightLightDescription[];
-
 extern const char kEnableNotificationScrollBarName[];
 extern const char kEnableNotificationScrollBarDescription[];
 
@@ -1916,9 +1910,6 @@
 extern const char kEnableAppDataSearchName[];
 extern const char kEnableAppDataSearchDescription[];
 
-extern const char kEnableAppsGridGapFeatureName[];
-extern const char kEnableAppsGridGapFeatureDescription[];
-
 extern const char kEnableArcUnifiedAudioFocusName[];
 extern const char kEnableArcUnifiedAudioFocusDescription[];
 
diff --git a/chrome/browser/media/media_engagement_autoplay_browsertest.cc b/chrome/browser/media/media_engagement_autoplay_browsertest.cc
index bf499cb..3ab630e 100644
--- a/chrome/browser/media/media_engagement_autoplay_browsertest.cc
+++ b/chrome/browser/media/media_engagement_autoplay_browsertest.cc
@@ -363,6 +363,6 @@
   ExpectAutoplayAllowedIfEnabled();
 }
 
-INSTANTIATE_TEST_CASE_P(/* no prefix */,
-                        MediaEngagementAutoplayBrowserTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         MediaEngagementAutoplayBrowserTest,
+                         testing::Bool());
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index cc3c5f8..ccfe73f 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -245,6 +245,7 @@
     case blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE:
     case blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE:
     case blink::MEDIA_DISPLAY_VIDEO_CAPTURE:
+    case blink::MEDIA_DISPLAY_AUDIO_CAPTURE:
       return desktop_stream_count_;
 
     case blink::MEDIA_NO_SERVICE:
diff --git a/chrome/browser/net/netinfo_network_quality_estimator_holdback_browsertest.cc b/chrome/browser/net/netinfo_network_quality_estimator_holdback_browsertest.cc
index 5921898..2617d6a 100644
--- a/chrome/browser/net/netinfo_network_quality_estimator_holdback_browsertest.cc
+++ b/chrome/browser/net/netinfo_network_quality_estimator_holdback_browsertest.cc
@@ -243,6 +243,6 @@
 
 // The network quality estimator web holdback is enabled only if the first
 // param is true.
-INSTANTIATE_TEST_CASE_P(,
-                        NetInfoNetworkQualityEstimatorHoldbackBrowserTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(,
+                         NetInfoNetworkQualityEstimatorHoldbackBrowserTest,
+                         testing::Bool());
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index 8c69d6e..6bc5cb5 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -1882,7 +1882,7 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #define INSTANTIATE_EXTENSION_TESTS(TestFixture)                          \
-  INSTANTIATE_TEST_CASE_P(                                                \
+  INSTANTIATE_TEST_SUITE_P(                                               \
       OnDiskApp, TestFixture,                                             \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,         \
                                   NetworkContextType::kOnDiskApp}),       \
@@ -1891,7 +1891,7 @@
                         TestCase({NetworkServiceState::kRestarted,        \
                                   NetworkContextType::kOnDiskApp})));     \
                                                                           \
-  INSTANTIATE_TEST_CASE_P(                                                \
+  INSTANTIATE_TEST_SUITE_P(                                               \
       InMemoryApp, TestFixture,                                           \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,         \
                                   NetworkContextType::kInMemoryApp}),     \
@@ -1900,7 +1900,7 @@
                         TestCase({NetworkServiceState::kRestarted,        \
                                   NetworkContextType::kInMemoryApp})));   \
                                                                           \
-  INSTANTIATE_TEST_CASE_P(                                                \
+  INSTANTIATE_TEST_SUITE_P(                                               \
       OnDiskAppWithIncognitoProfile, TestFixture,                         \
       ::testing::Values(                                                  \
           TestCase({NetworkServiceState::kDisabled,                       \
@@ -1915,7 +1915,7 @@
 
 #define INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(TestFixture)               \
   INSTANTIATE_EXTENSION_TESTS(TestFixture)                                 \
-  INSTANTIATE_TEST_CASE_P(                                                 \
+  INSTANTIATE_TEST_SUITE_P(                                                \
       SystemNetworkContext, TestFixture,                                   \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kSystem}),           \
@@ -1924,7 +1924,7 @@
                         TestCase({NetworkServiceState::kRestarted,         \
                                   NetworkContextType::kSystem})));         \
                                                                            \
-  INSTANTIATE_TEST_CASE_P(                                                 \
+  INSTANTIATE_TEST_SUITE_P(                                                \
       SafeBrowsingNetworkContext, TestFixture,                             \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kSafeBrowsing}),     \
@@ -1933,7 +1933,7 @@
                         TestCase({NetworkServiceState::kRestarted,         \
                                   NetworkContextType::kSafeBrowsing})));   \
                                                                            \
-  INSTANTIATE_TEST_CASE_P(                                                 \
+  INSTANTIATE_TEST_SUITE_P(                                                \
       ProfileMainNetworkContext, TestFixture,                              \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kProfile}),          \
@@ -1942,7 +1942,7 @@
                         TestCase({NetworkServiceState::kRestarted,         \
                                   NetworkContextType::kProfile})));        \
                                                                            \
-  INSTANTIATE_TEST_CASE_P(                                                 \
+  INSTANTIATE_TEST_SUITE_P(                                                \
       IncognitoProfileMainNetworkContext, TestFixture,                     \
       ::testing::Values(TestCase({NetworkServiceState::kDisabled,          \
                                   NetworkContextType::kIncognitoProfile}), \
diff --git a/chrome/browser/net/network_request_metrics_browsertest.cc b/chrome/browser/net/network_request_metrics_browsertest.cc
index 8bd1882..3bac907a 100644
--- a/chrome/browser/net/network_request_metrics_browsertest.cc
+++ b/chrome/browser/net/network_request_metrics_browsertest.cc
@@ -609,12 +609,12 @@
                   NetworkAccessed::kNoNetworkAccessed);
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        NetworkRequestMetricsBrowserTest,
-                        testing::Values(RequestType::kMainFrame,
-                                        RequestType::kSubFrame,
-                                        RequestType::kImage,
-                                        RequestType::kScript));
+INSTANTIATE_TEST_SUITE_P(,
+                         NetworkRequestMetricsBrowserTest,
+                         testing::Values(RequestType::kMainFrame,
+                                         RequestType::kSubFrame,
+                                         RequestType::kImage,
+                                         RequestType::kScript));
 
 }  //  namespace
 }  // namespace content
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index 66bb041..fcc1cf1 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -177,13 +177,13 @@
   EXPECT_TRUE(base::PathExists(expected_cache_path));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     /* No test prefix */,
     ProfileNetworkContextServiceBrowsertest,
     ::testing::Values(NetworkServiceState::kDisabled,
                       NetworkServiceState::kEnabled));
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     /* No test prefix */,
     ProfileNetworkContextServiceDiskCacheDirBrowsertest,
     ::testing::Values(NetworkServiceState::kDisabled,
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index 5175d646..cbf7395b 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -268,6 +268,6 @@
   RunStubResolverConfigTests(GetParam());
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        SystemNetworkContextManagerStubResolverBrowsertest,
-                        ::testing::Values(false, true));
+INSTANTIATE_TEST_SUITE_P(,
+                         SystemNetworkContextManagerStubResolverBrowsertest,
+                         ::testing::Values(false, true));
diff --git a/chrome/browser/performance_monitor/system_monitor.cc b/chrome/browser/performance_monitor/system_monitor.cc
new file mode 100644
index 0000000..16964989
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor.cc
@@ -0,0 +1,179 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_monitor/system_monitor.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/task/post_task.h"
+#include "base/task_runner_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/performance_monitor/system_monitor_helper_win.h"
+#elif defined(OS_POSIX)
+#include "chrome/browser/performance_monitor/system_monitor_helper_posix.h"
+#endif
+
+namespace performance_monitor {
+
+namespace {
+
+using MetricRefreshFrequencies =
+    SystemMonitor::SystemObserver::MetricRefreshFrequencies;
+
+// The global instance.
+SystemMonitor* g_system_metrics_monitor = nullptr;
+
+}  // namespace
+
+SystemMonitor::SystemMonitor(std::unique_ptr<SystemMonitorHelper> helper)
+    : blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(),
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
+      async_helper_(helper.release(),
+                    base::OnTaskRunnerDeleter(blocking_task_runner_)),
+      weak_factory_(this) {
+  DCHECK(!g_system_metrics_monitor);
+  g_system_metrics_monitor = this;
+}
+
+SystemMonitor::~SystemMonitor() {
+  DCHECK_EQ(this, g_system_metrics_monitor);
+  g_system_metrics_monitor = nullptr;
+}
+
+// static
+std::unique_ptr<SystemMonitor> SystemMonitor::Create() {
+  DCHECK(!g_system_metrics_monitor);
+#if defined(OS_WIN)
+  SystemMonitor* monitor =
+      new SystemMonitor(base::WrapUnique(new win::SystemMonitorHelperWin()));
+#elif defined(OS_POSIX)
+  SystemMonitor* monitor =
+      new SystemMonitor(base::WrapUnique(new SystemMonitorHelperPosix()));
+#else
+#error Unsupported platform
+#endif
+  return base::WrapUnique(monitor);
+}
+
+// static
+SystemMonitor* SystemMonitor::Get() {
+  return g_system_metrics_monitor;
+}
+
+void SystemMonitor::SystemObserver::OnFreePhysicalMemoryMbSample(
+    base::Optional<int> free_phys_memory_mb) {
+  NOTREACHED();
+}
+
+void SystemMonitor::AddOrUpdateObserver(
+    SystemMonitor::SystemObserver* observer,
+    MetricRefreshFrequencies metrics_and_frequencies) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!observers_.HasObserver(observer))
+    observers_.AddObserver(observer);
+  observer_metrics_[observer] = std::move(metrics_and_frequencies);
+  UpdateObservedMetricsSet();
+}
+
+void SystemMonitor::RemoveObserver(SystemMonitor::SystemObserver* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.RemoveObserver(observer);
+  observer_metrics_.erase(observer);
+  UpdateObservedMetricsSet();
+}
+
+SystemMonitor::MetricsRefresh::MetricsRefresh() = default;
+SystemMonitor::MetricsRefresh::MetricsRefresh(MetricsRefresh&&) = default;
+
+void SystemMonitor::UpdateObservedMetricsSet() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Reset the current metric frequencies.
+  metrics_refresh_frequencies_ = MetricRefreshFrequencies();
+
+  // Iterates over the |observer_metrics_| list to find the highest refresh
+  // frequency for each metric.
+  for (const auto& iter : observer_metrics_) {
+    metrics_refresh_frequencies_.free_phys_memory_mb_frequency =
+        std::max(metrics_refresh_frequencies_.free_phys_memory_mb_frequency,
+                 iter.second.free_phys_memory_mb_frequency);
+  }
+
+  // Check if there's still some metrics being tracked.
+  bool should_refresh_metrics =
+      (metrics_refresh_frequencies_.free_phys_memory_mb_frequency >
+       SamplingFrequency::kNoSampling);
+
+  if (should_refresh_metrics) {
+    base::TimeDelta refresh_frequency =
+        async_helper_->GetRefreshInterval(metrics_refresh_frequencies_);
+    if (!refresh_timer_.IsRunning() ||
+        refresh_frequency != refresh_timer_.GetCurrentDelay()) {
+      refresh_timer_.Start(FROM_HERE, refresh_frequency,
+                           base::BindRepeating(&SystemMonitor::RefreshCallback,
+                                               base::Unretained(this)));
+    }
+    // Sanity check.
+    DCHECK_EQ(refresh_timer_.GetCurrentDelay(), refresh_frequency);
+  } else {
+    refresh_timer_.Stop();
+  }
+
+  // Start or stop the timer if needed.
+  if (refresh_timer_.IsRunning() && !should_refresh_metrics) {
+    refresh_timer_.Stop();
+  } else if (should_refresh_metrics) {
+    base::TimeDelta refresh_frequency =
+        async_helper_->GetRefreshInterval(metrics_refresh_frequencies_);
+    // Stop the timer if the refresh frequency has changed.
+    if (refresh_timer_.IsRunning() &&
+        refresh_frequency != refresh_timer_.GetCurrentDelay()) {
+      refresh_timer_.Stop();
+    }
+    if (!refresh_timer_.IsRunning()) {
+      refresh_timer_.Start(FROM_HERE, refresh_frequency,
+                           base::BindRepeating(&SystemMonitor::RefreshCallback,
+                                               base::Unretained(this)));
+    }
+  }
+}
+
+void SystemMonitor::RefreshCallback() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&SystemMonitorHelper::RefreshMetrics,
+                     base::Unretained(async_helper_.get()),
+                     metrics_refresh_frequencies_, base::TimeTicks::Now()),
+      base::BindOnce(&SystemMonitor::NotifyObservers,
+                     weak_factory_.GetWeakPtr()));
+
+  refresh_timer_.Start(FROM_HERE, refresh_timer_.GetCurrentDelay(),
+                       base::BindRepeating(&SystemMonitor::RefreshCallback,
+                                           base::Unretained(this)));
+}
+
+void SystemMonitor::NotifyObservers(const MetricsRefresh& metrics) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_NE(SamplingFrequency::kNoSampling,
+            metrics.free_phys_memory_mb.refresh_reason);
+
+  // Iterate over the observers and notify them if a metric has been refreshed
+  // at the requested frequency.
+  for (auto& observer : observers_) {
+    const auto& iter = observer_metrics_.find(&observer);
+    DCHECK(iter != observer_metrics_.end());
+    if (metrics.free_phys_memory_mb.refresh_reason <=
+        iter->second.free_phys_memory_mb_frequency) {
+      iter->first->OnFreePhysicalMemoryMbSample(
+          metrics.free_phys_memory_mb.metric_value);
+    }
+  }
+}
+
+}  // namespace performance_monitor
diff --git a/chrome/browser/performance_monitor/system_monitor.h b/chrome/browser/performance_monitor/system_monitor.h
new file mode 100644
index 0000000..ece4fdf
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor.h
@@ -0,0 +1,205 @@
+// Copyright 2019 The Chromium Authors. 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_PERFORMANCE_MONITOR_SYSTEM_MONITOR_H_
+#define CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace performance_monitor {
+
+class SystemMonitorHelper;
+
+// Monitors various various system metrics such as free memory, disk idle time,
+// etc.
+//
+// Must be created and used from the UI thread.
+//
+// Users of this class need to subscribe as observers via the
+// AddOrUpdateObserver method. They need to specify which metrics they're
+// interested in and at which frequency they should be refreshed. This set of
+// metrics and frequencies can then be updated at runtime.
+//
+// Platforms that want to use this class need to provide a platform specific
+// implementation of the SystemMonitorHelper class and update
+// SystemMonitor::Create.
+class SystemMonitor {
+ public:
+  // The frequency at which a metric will be collected. Exact frequencies are
+  // implementation details determined by experimentation.
+  //
+  // NOTE: Frequencies must be listed in increasing order in this enum.
+  enum class SamplingFrequency : uint32_t {
+    kNoSampling = 0,
+    kDefaultFrequency = 1,
+  };
+
+  virtual ~SystemMonitor();
+
+  // Creates and returns the application-wide SystemMonitor. Can only be called
+  // if no SystemMonitor instance exists in the current process. The caller
+  // owns the created instance. The current process' instance can be retrieved
+  // with Get().
+  static std::unique_ptr<SystemMonitor> Create();
+
+  // Get the application-wide SystemMonitor (if not present, returns
+  // nullptr).
+  static SystemMonitor* Get();
+
+  // Observer that should be notified when new samples are available.
+  //
+  // When being registered, an observer should declare the metrics it want to
+  // track and their refresh frequency.
+  class SystemObserver : public base::CheckedObserver {
+   public:
+    // A struct that associates metrics with their refresh frequencies.
+    struct MetricRefreshFrequencies {
+      SamplingFrequency free_phys_memory_mb_frequency =
+          SamplingFrequency::kNoSampling;
+    };
+
+    ~SystemObserver() override = default;
+
+    // Reports the amount of free physical memory, in MB. |free_phys_memory_mb|
+    // can be equal to nullopt if the monitor failed to retrieve this value.
+    virtual void OnFreePhysicalMemoryMbSample(
+        base::Optional<int> free_phys_memory_mb);
+  };
+  using ObserverToFrequenciesMap =
+      base::flat_map<SystemObserver*, SystemObserver::MetricRefreshFrequencies>;
+
+  // Adds |observer| as an observer and updates the metrics to collect and their
+  // frequencies based on |metrics_frequencies|. If this observer is already
+  // in the list then this simply updates the list of metrics to collect or
+  // their frequency.
+  void AddOrUpdateObserver(
+      SystemObserver* observer,
+      SystemObserver::MetricRefreshFrequencies metrics_frequencies);
+
+  // Removes |observer| from the observer list. After this call, the observer
+  // will not receive notifications for any metric.
+  void RemoveObserver(SystemObserver* observer);
+
+  const SystemObserver::MetricRefreshFrequencies&
+  metric_refresh_frequencies_for_testing() const {
+    return metrics_refresh_frequencies_;
+  }
+
+  bool IsRefreshTimerRunningForTesting() { return refresh_timer_.IsRunning(); }
+
+  void SetHelperForTesting(std::unique_ptr<SystemMonitorHelper> helper) {
+    async_helper_.reset(helper.release());
+  }
+
+ protected:
+  friend class SystemMonitorHelper;
+
+  // Creates SystemMonitor. Only one SystemMonitor instance per
+  // application is allowed.
+  explicit SystemMonitor(std::unique_ptr<SystemMonitorHelper> helper);
+
+  // A struct that stores a refreshed metric and the reason why it has been
+  // refreshed.
+  template <typename T>
+  struct MetricAndRefreshReason {
+    MetricAndRefreshReason() {}
+    MetricAndRefreshReason(base::Optional<T> value, SamplingFrequency reason)
+        : metric_value(value), refresh_reason(reason) {}
+    base::Optional<T> metric_value = base::nullopt;
+    SamplingFrequency refresh_reason = SamplingFrequency::kNoSampling;
+  };
+
+  // Struct that will receive the refreshed metrics in the refresh callback.
+  struct MetricsRefresh {
+    MetricsRefresh();
+    MetricsRefresh(MetricsRefresh&& o);
+    MetricAndRefreshReason<int> free_phys_memory_mb;
+  };
+
+ private:
+  // Updates |observed_metrics_| with the list of metrics that need to be
+  // tracked.
+  void UpdateObservedMetricsSet();
+
+  // Function that gets called by every time the refresh callback triggers.
+  void RefreshCallback();
+
+  // Notify the observers with the refreshed metrics.
+  void NotifyObservers(const MetricsRefresh& metrics);
+
+  // The list of observers.
+  base::ObserverList<SystemObserver> observers_;
+
+  // A map that associates an observer to the metrics it's interested in.
+  ObserverToFrequenciesMap observer_metrics_;
+
+  // The current metrics that are being observed and the corresponding refresh
+  // frequency.
+  SystemObserver::MetricRefreshFrequencies metrics_refresh_frequencies_;
+
+  // The timer responsible of refreshing the metrics and notifying the
+  // observers.
+  base::OneShotTimer refresh_timer_;
+
+  // The task runner used to run all the blocking operations.
+  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+
+  // The SystemMonitorHelper instance responsible for running all asynchronous
+  // operations.
+  std::unique_ptr<SystemMonitorHelper, base::OnTaskRunnerDeleter> async_helper_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<SystemMonitor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitor);
+};
+
+// Helper class used to run all the blocking operations posted by SystemMonitor
+// on a sequence with the |MayBlock()| trait.
+//
+// Instances of this class should only be destructed once all the posted tasks
+// have been run, in practice it means that they should ideally be stored in a
+// std::unique_ptr<AsyncHelper, base::OnTaskRunnerDeleter>.
+class SystemMonitorHelper {
+ public:
+  using MetricsRefresh = SystemMonitor::MetricsRefresh;
+  template <typename T>
+  using MetricAndRefreshReason = SystemMonitor::MetricAndRefreshReason<T>;
+
+  SystemMonitorHelper() = default;
+  virtual ~SystemMonitorHelper() = default;
+
+  // Returns the refresh interval that should be used by the SystemMonitor
+  // refresh timer based on the current configuration of its observers. If no
+  // metrics are being observed then this returns base::TimeDelta::Max().
+  //
+  // This can be called from any sequence.
+  virtual base::TimeDelta GetRefreshInterval(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+          metrics_and_frequencies) = 0;
+
+  // Refresh the metrics according to |metrics_and_frequencies|. |refresh_time|
+  // indicates at which time the refresh has been requested.
+  virtual MetricsRefresh RefreshMetrics(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+          metrics_and_frequencies,
+      const base::TimeTicks& refresh_time) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitorHelper);
+};
+
+}  // namespace performance_monitor
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_H_
diff --git a/chrome/browser/performance_monitor/system_monitor_helper_posix.cc b/chrome/browser/performance_monitor/system_monitor_helper_posix.cc
new file mode 100644
index 0000000..12df731
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_helper_posix.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_monitor/system_monitor_helper_posix.h"
+
+namespace performance_monitor {
+
+base::TimeDelta SystemMonitorHelperPosix::GetRefreshInterval(
+    const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+        metrics_and_frequencies) {
+  NOTIMPLEMENTED();
+  return base::TimeDelta::Max();
+}
+
+SystemMonitorHelper::MetricsRefresh SystemMonitorHelperPosix::RefreshMetrics(
+    const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+        metrics_and_frequencies,
+    const base::TimeTicks& refresh_time) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+}  // namespace performance_monitor
diff --git a/chrome/browser/performance_monitor/system_monitor_helper_posix.h b/chrome/browser/performance_monitor/system_monitor_helper_posix.h
new file mode 100644
index 0000000..f8bf83b
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_helper_posix.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. 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_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_POSIX_H_
+#define CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_POSIX_H_
+
+#include "chrome/browser/performance_monitor/system_monitor.h"
+
+namespace performance_monitor {
+
+// Posix implementation of the SystemMonitorHelper class. Do not use
+// this directly, instead access the system metrics via the SystemMonitor
+// interface.
+class SystemMonitorHelperPosix : public SystemMonitorHelper {
+ public:
+  ~SystemMonitorHelperPosix() override = default;
+
+ protected:
+  friend class ::performance_monitor::SystemMonitor;
+
+  // Protected constructor so this can only be created by SystemMonitor.
+  SystemMonitorHelperPosix() = default;
+
+  // SystemMonitorHelper:
+  base::TimeDelta GetRefreshInterval(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+          metrics_and_frequencies) override;
+  MetricsRefresh RefreshMetrics(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+          metrics_and_frequencies,
+      const base::TimeTicks& refresh_time) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitorHelperPosix);
+};
+
+}  // namespace performance_monitor
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_POSIX_H_
diff --git a/chrome/browser/performance_monitor/system_monitor_helper_win.cc b/chrome/browser/performance_monitor/system_monitor_helper_win.cc
new file mode 100644
index 0000000..6cd6f6c7
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_helper_win.cc
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_monitor/system_monitor_helper_win.h"
+
+#include <windows.h>
+
+namespace performance_monitor {
+namespace win {
+
+namespace {
+
+// The refresh period of the free physical memory metric when being tracked at
+// the regular frequency.
+constexpr base::TimeDelta kRefreshIntervalPhysMemoryMbRegFreq =
+    base::TimeDelta::FromSeconds(2);
+
+const DWORDLONG kMBBytes = 1024 * 1024;
+
+// Returns the amount of physical memory available on the system.
+base::Optional<int> GetFreePhysMemoryMb() {
+  MEMORYSTATUSEX mem_status;
+  mem_status.dwLength = sizeof(mem_status);
+  if (!::GlobalMemoryStatusEx(&mem_status))
+    return base::nullopt;
+
+  return (mem_status.ullAvailPhys / kMBBytes);
+}
+
+}  // namespace
+
+SystemMonitorHelperWin::SystemMonitorHelperWin() {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+base::TimeDelta SystemMonitorHelperWin::GetRefreshInterval(
+    const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+        metrics_and_frequencies) {
+  if (metrics_and_frequencies.free_phys_memory_mb_frequency ==
+      SystemMonitor::SamplingFrequency::kDefaultFrequency) {
+    return kRefreshIntervalPhysMemoryMbRegFreq;
+  }
+  return base::TimeDelta::Max();
+}
+
+SystemMonitorHelperWin::MetricsRefresh SystemMonitorHelperWin::RefreshMetrics(
+    const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+        metrics_and_frequencies,
+    const base::TimeTicks& refresh_time) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SystemMonitorHelper::MetricsRefresh metrics;
+
+  if ((refresh_time - last_phys_memory_refresh_default_freq_) >=
+      kRefreshIntervalPhysMemoryMbRegFreq) {
+    last_phys_memory_refresh_default_freq_ = refresh_time;
+    metrics.free_phys_memory_mb =
+        SystemMonitorHelper::MetricAndRefreshReason<int>(
+            GetFreePhysMemoryMb(),
+            SystemMonitor::SamplingFrequency::kDefaultFrequency);
+  }
+
+  return metrics;
+}
+
+}  // namespace win
+}  // namespace performance_monitor
diff --git a/chrome/browser/performance_monitor/system_monitor_helper_win.h b/chrome/browser/performance_monitor/system_monitor_helper_win.h
new file mode 100644
index 0000000..7bf1250b
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_helper_win.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. 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_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_WIN_H_
+#define CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_WIN_H_
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/performance_monitor/system_monitor.h"
+
+namespace performance_monitor {
+namespace win {
+
+// Windows specific implementation of the SystemMonitorHelper class. Do not use
+// this directly, instead access the system metrics via the SystemMonitor
+// interface.
+class SystemMonitorHelperWin : public SystemMonitorHelper {
+ public:
+  ~SystemMonitorHelperWin() override = default;
+
+ protected:
+  friend class ::performance_monitor::SystemMonitor;
+  friend class SystemMonitorHelperWinTest;
+
+  // Protected constructor so this can only be created by SystemMonitor.
+  SystemMonitorHelperWin();
+
+  // SystemMonitorHelperWin:
+  base::TimeDelta GetRefreshInterval(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+          metrics_and_frequencies) override;
+  MetricsRefresh RefreshMetrics(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+          metrics_and_frequencies,
+      const base::TimeTicks& refresh_time) override;
+
+ private:
+  // The last time the free physical memory value has been refreshed at the
+  // default frequency.
+  base::TimeTicks last_phys_memory_refresh_default_freq_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitorHelperWin);
+};
+
+}  // namespace win
+}  // namespace performance_monitor
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MONITOR_SYSTEM_MONITOR_HELPER_WIN_H_
diff --git a/chrome/browser/performance_monitor/system_monitor_helper_win_unittest.cc b/chrome/browser/performance_monitor/system_monitor_helper_win_unittest.cc
new file mode 100644
index 0000000..05fe0687
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_helper_win_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_monitor/system_monitor_helper_win.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_monitor {
+namespace win {
+
+namespace {
+
+class TestSystemMonitorHelperWin : public SystemMonitorHelperWin {
+ public:
+  TestSystemMonitorHelperWin() = default;
+  ~TestSystemMonitorHelperWin() override = default;
+
+  using SystemMonitorHelperWin::RefreshMetrics;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSystemMonitorHelperWin);
+};
+
+using SystemMonitorHelperWinTest = testing::Test;
+
+TEST_F(SystemMonitorHelperWinTest, GetFreeMemory) {
+  TestSystemMonitorHelperWin helper;
+  auto refreshed_metrics = helper.RefreshMetrics(
+      {.free_phys_memory_mb_frequency =
+           SystemMonitor::SamplingFrequency::kDefaultFrequency},
+      base::TimeTicks::Now());
+  EXPECT_NE(base::nullopt, refreshed_metrics.free_phys_memory_mb.metric_value);
+}
+
+}  // namespace
+
+}  // namespace win
+}  // namespace performance_monitor
diff --git a/chrome/browser/performance_monitor/system_monitor_unittest.cc b/chrome/browser/performance_monitor/system_monitor_unittest.cc
new file mode 100644
index 0000000..ff50804a
--- /dev/null
+++ b/chrome/browser/performance_monitor/system_monitor_unittest.cc
@@ -0,0 +1,174 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_monitor/system_monitor.h"
+
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_monitor {
+
+namespace {
+
+using SystemObserver = SystemMonitor::SystemObserver;
+using MetricsRefreshFrequencies = SystemObserver::MetricRefreshFrequencies;
+
+constexpr base::TimeDelta kRefreshInterval = base::TimeDelta::FromSeconds(1);
+
+bool operator==(const MetricsRefreshFrequencies& lhs,
+                const MetricsRefreshFrequencies& rhs) {
+  return std::tie(lhs.free_phys_memory_mb_frequency) ==
+         std::tie(rhs.free_phys_memory_mb_frequency);
+}
+
+class MockMetricsMonitorObserver : public SystemObserver {
+ public:
+  ~MockMetricsMonitorObserver() override {}
+  MOCK_METHOD1(OnFreePhysicalMemoryMbSample,
+               void(base::Optional<int> free_phys_memory_mb));
+};
+
+class TestSystemMonitorHelper : public SystemMonitorHelper {
+ public:
+  TestSystemMonitorHelper() = default;
+  ~TestSystemMonitorHelper() override = default;
+
+ protected:
+  // SystemMonitorHelper:
+  base::TimeDelta GetRefreshInterval(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies&
+          metrics_and_frequencies) override {
+    return kRefreshInterval;
+  }
+  MetricsRefresh RefreshMetrics(
+      const SystemMonitor::SystemObserver::MetricRefreshFrequencies
+          metrics_and_frequencies,
+      const base::TimeTicks& refresh_time) override {
+    SystemMonitorHelper::MetricsRefresh metrics;
+    metrics.free_phys_memory_mb =
+        SystemMonitorHelper::MetricAndRefreshReason<int>(
+            42, SystemMonitor::SamplingFrequency::kDefaultFrequency);
+    return metrics;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSystemMonitorHelper);
+};
+
+class SystemMonitorTest : public testing::Test {
+ protected:
+  SystemMonitorTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
+    EXPECT_EQ(nullptr, SystemMonitor::Get());
+    system_monitor_ = SystemMonitor::Create();
+  }
+
+  std::unique_ptr<SystemMonitor> system_monitor_;
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitorTest);
+};
+
+TEST_F(SystemMonitorTest, GetReturnsSingleInstance) {
+  EXPECT_EQ(system_monitor_.get(), SystemMonitor::Get());
+  system_monitor_.reset();
+  EXPECT_EQ(nullptr, SystemMonitor::Get());
+}
+
+TEST_F(SystemMonitorTest, AddAndUpdateObservers) {
+  SystemObserver obs1;
+  SystemObserver obs2;
+  SystemObserver obs3;
+
+  // The first observer doesn't observe anything yet.
+  MetricsRefreshFrequencies obs1_metrics_frequencies = {};
+  system_monitor_->AddOrUpdateObserver(&obs1, obs1_metrics_frequencies);
+  MetricsRefreshFrequencies expected_metrics_frequencies =
+      obs1_metrics_frequencies;
+  auto observed_metrics_and_frequencies =
+      system_monitor_->metric_refresh_frequencies_for_testing();
+  EXPECT_TRUE(observed_metrics_and_frequencies == expected_metrics_frequencies);
+
+  // Add a second observer that observes the amount of free memory at the
+  // default frequency.
+  MetricsRefreshFrequencies obs2_metrics_frequencies = {
+      .free_phys_memory_mb_frequency =
+          SystemMonitor::SamplingFrequency::kDefaultFrequency};
+  system_monitor_->AddOrUpdateObserver(&obs2, obs2_metrics_frequencies);
+  expected_metrics_frequencies.free_phys_memory_mb_frequency =
+      SystemMonitor::SamplingFrequency::kDefaultFrequency;
+  observed_metrics_and_frequencies =
+      system_monitor_->metric_refresh_frequencies_for_testing();
+  EXPECT_TRUE(observed_metrics_and_frequencies == expected_metrics_frequencies);
+
+  // Stop observing any metric with the second observer.
+  obs2_metrics_frequencies.free_phys_memory_mb_frequency =
+      SystemMonitor::SamplingFrequency::kNoSampling;
+  system_monitor_->AddOrUpdateObserver(&obs2, obs2_metrics_frequencies);
+  expected_metrics_frequencies.free_phys_memory_mb_frequency =
+      SystemMonitor::SamplingFrequency::kNoSampling;
+  observed_metrics_and_frequencies =
+      system_monitor_->metric_refresh_frequencies_for_testing();
+  EXPECT_TRUE(observed_metrics_and_frequencies == expected_metrics_frequencies);
+
+  // Add a second observer that observes the amount of free memory at the
+  // default frequency.
+  MetricsRefreshFrequencies obs3_metrics_frequencies = {
+      .free_phys_memory_mb_frequency =
+          SystemMonitor::SamplingFrequency::kDefaultFrequency};
+  system_monitor_->AddOrUpdateObserver(&obs3, obs3_metrics_frequencies);
+  expected_metrics_frequencies.free_phys_memory_mb_frequency =
+      SystemMonitor::SamplingFrequency::kDefaultFrequency;
+  observed_metrics_and_frequencies =
+      system_monitor_->metric_refresh_frequencies_for_testing();
+  EXPECT_TRUE(observed_metrics_and_frequencies == expected_metrics_frequencies);
+
+  // Remove the third observe, ensure that no metrics are observed anymore.
+  system_monitor_->RemoveObserver(&obs3);
+  expected_metrics_frequencies.free_phys_memory_mb_frequency =
+      SystemMonitor::SamplingFrequency::kNoSampling;
+  observed_metrics_and_frequencies =
+      system_monitor_->metric_refresh_frequencies_for_testing();
+  EXPECT_TRUE(observed_metrics_and_frequencies == expected_metrics_frequencies);
+}
+
+TEST_F(SystemMonitorTest, ObserverGetsCalled) {
+  system_monitor_->SetHelperForTesting(
+      std::make_unique<TestSystemMonitorHelper>());
+
+  ::testing::StrictMock<MockMetricsMonitorObserver> mock_observer_1;
+  system_monitor_->AddOrUpdateObserver(
+      &mock_observer_1,
+      {.free_phys_memory_mb_frequency =
+           SystemMonitor::SamplingFrequency::kDefaultFrequency});
+
+  ::testing::StrictMock<MockMetricsMonitorObserver> mock_observer_2;
+  system_monitor_->AddOrUpdateObserver(&mock_observer_2, {});
+
+  // Ensure that we get several samples to verify that the timer logic works.
+  EXPECT_CALL(mock_observer_1,
+              OnFreePhysicalMemoryMbSample(::testing::Optional(42)))
+      .Times(2);
+
+  // The second observer shouldn't be called.
+  EXPECT_CALL(mock_observer_2, OnFreePhysicalMemoryMbSample(::testing::_))
+      .Times(0);
+
+  // Fast forward by enough time to get multiple samples and wait for the tasks
+  // to complete.
+  scoped_task_environment_.FastForwardBy(2 * kRefreshInterval);
+  scoped_task_environment_.RunUntilIdle();
+
+  ::testing::Mock::VerifyAndClear(&mock_observer_1);
+  ::testing::Mock::VerifyAndClear(&mock_observer_2);
+}
+
+}  // namespace
+
+}  // namespace performance_monitor
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 28d790d95..439ddb1 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -3458,7 +3458,7 @@
     speaking_requested_ = true;
     // Dispatch the end of speaking back to the page.
     content::TtsController::GetInstance()->OnTtsEvent(
-        utterance_id, content::TTS_EVENT_END,
+        utterance_id, content::TTS_EVENT_END, 0,
         static_cast<int>(utterance.size()), std::string());
     return true;
   }
diff --git a/chrome/browser/resources/bluetooth_internals/adapter_page.js b/chrome/browser/resources/bluetooth_internals/adapter_page.js
index b032be0..b5b95ea 100644
--- a/chrome/browser/resources/bluetooth_internals/adapter_page.js
+++ b/chrome/browser/resources/bluetooth_internals/adapter_page.js
@@ -7,8 +7,6 @@
  */
 
 cr.define('adapter_page', function() {
-  /** @const */ var Page = cr.ui.pageManager.Page;
-
   var PROPERTY_NAMES = {
     address: 'Address',
     name: 'Name',
@@ -21,43 +19,39 @@
 
   /**
    * Page that contains an ObjectFieldSet that displays the latest AdapterInfo.
-   * @constructor
-   * @extends {cr.ui.pageManager.Page}
    */
-  function AdapterPage() {
-    Page.call(this, 'adapter', 'Adapter', 'adapter');
+  class AdapterPage extends cr.ui.pageManager.Page {
+    constructor() {
+      super('adapter', 'Adapter', 'adapter');
 
-    this.adapterFieldSet = new object_fieldset.ObjectFieldSet();
-    this.adapterFieldSet.setPropertyDisplayNames(PROPERTY_NAMES);
-    this.pageDiv.appendChild(this.adapterFieldSet);
+      this.adapterFieldSet = new object_fieldset.ObjectFieldSet();
+      this.adapterFieldSet.setPropertyDisplayNames(PROPERTY_NAMES);
+      this.pageDiv.appendChild(this.adapterFieldSet);
 
-    this.refreshBtn_ = $('adapter-refresh-btn');
-    this.refreshBtn_.addEventListener('click', function() {
-      this.refreshBtn_.disabled = true;
-      this.pageDiv.dispatchEvent(new CustomEvent('refreshpressed'));
-    }.bind(this));
-  }
-
-  AdapterPage.prototype = {
-    __proto__: Page.prototype,
+      this.refreshBtn_ = $('adapter-refresh-btn');
+      this.refreshBtn_.addEventListener('click', event => {
+        this.refreshBtn_.disabled = true;
+        this.pageDiv.dispatchEvent(new CustomEvent('refreshpressed'));
+      });
+    }
 
     /**
      * Sets the information to display in fieldset.
      * @param {!bluetooth.mojom.AdapterInfo} info
      */
-    setAdapterInfo: function(info) {
+    setAdapterInfo(info) {
       this.adapterFieldSet.setObject(info);
       this.refreshBtn_.disabled = false;
-    },
+    }
 
     /**
      * Redraws the fieldset displaying the adapter info.
      */
-    redraw: function() {
+    redraw() {
       this.adapterFieldSet.redraw();
       this.refreshBtn_.disabled = false;
-    },
-  };
+    }
+  }
 
   return {
     AdapterPage: AdapterPage,
diff --git a/chrome/browser/resources/bluetooth_internals/device_collection.js b/chrome/browser/resources/bluetooth_internals/device_collection.js
index f9af143..a867246 100644
--- a/chrome/browser/resources/bluetooth_internals/device_collection.js
+++ b/chrome/browser/resources/bluetooth_internals/device_collection.js
@@ -24,22 +24,21 @@
   /**
    * Collection of devices. Extends ArrayDataModel which provides a set of
    * functions and events that notifies observers when the collection changes.
-   * @constructor
-   * @param {!Array<!bluetooth.mojom.DeviceInfo>} array The starting
-   *     collection of devices.
-   * @extends {cr.ui.ArrayDataModel}
    */
-  var DeviceCollection = function(array) {
-    cr.ui.ArrayDataModel.call(this, array);
-  };
-  DeviceCollection.prototype = {
-    __proto__: cr.ui.ArrayDataModel.prototype,
+  class DeviceCollection extends cr.ui.ArrayDataModel {
+    /**
+     * @param {!Array<!bluetooth.mojom.DeviceInfo>} array The starting
+     *     collection of devices.
+     */
+    constructor(array) {
+      super(array);
+    }
 
     /**
      * Finds the Device in the collection with the matching address.
      * @param {string} address
      */
-    getByAddress: function(address) {
+    getByAddress(address) {
       for (var i = 0; i < this.length; i++) {
         var device = this.item(i);
         if (address == device.address) {
@@ -47,13 +46,13 @@
         }
       }
       return null;
-    },
+    }
 
     /**
      * Adds or updates a Device with new DeviceInfo.
      * @param {!bluetooth.mojom.DeviceInfo} deviceInfo
      */
-    addOrUpdate: function(deviceInfo) {
+    addOrUpdate(deviceInfo) {
       deviceInfo.removed = false;
       var oldDeviceInfo = this.getByAddress(deviceInfo.address);
 
@@ -72,30 +71,30 @@
         deviceInfo.connectionStatus = ConnectionStatus.DISCONNECTED;
         this.push(deviceInfo);
       }
-    },
+    }
 
     /**
      * Marks the Device as removed.
      * @param {!bluetooth.mojom.DeviceInfo} deviceInfo
      */
-    remove: function(deviceInfo) {
+    remove(deviceInfo) {
       var device = this.getByAddress(deviceInfo.address);
       assert(device, 'Device does not exist.');
       device.removed = true;
       this.updateIndex(this.indexOf(device));
-    },
+    }
 
     /**
      * Updates the device connection status.
      * @param {string} address The address of the device.
      * @param {number} status The new connection status.
      */
-    updateConnectionStatus: function(address, status) {
+    updateConnectionStatus(address, status) {
       var device = assert(this.getByAddress(address), 'Device does not exist');
       device.connectionStatus = status;
       this.updateIndex(this.indexOf(device));
-    },
-  };
+    }
+  }
 
   return {
     ConnectionStatus: ConnectionStatus,
diff --git a/chrome/browser/resources/bluetooth_internals/device_details_page.js b/chrome/browser/resources/bluetooth_internals/device_details_page.js
index 1a807e7..3f10603 100644
--- a/chrome/browser/resources/bluetooth_internals/device_details_page.js
+++ b/chrome/browser/resources/bluetooth_internals/device_details_page.js
@@ -31,65 +31,64 @@
    * sections: Status and Services. The Status section displays information from
    * the DeviceInfo object and the Services section contains a ServiceList
    * compononent that lists all of the active services on the device.
-   * @constructor
-   * @param {string} id
-   * @param {!bluetooth.mojom.DeviceInfo} deviceInfo
-   * @extends {cr.ui.pageManager.Page}
    */
-  function DeviceDetailsPage(id, deviceInfo) {
-    Page.call(this, id, deviceInfo.nameForDisplay, id);
+  class DeviceDetailsPage extends Page {
+    /**
+     * @param {string} id
+     * @param {!bluetooth.mojom.DeviceInfo} deviceInfo
+     */
+    constructor(id, deviceInfo) {
+      super(id, deviceInfo.nameForDisplay, id);
 
-    /** @type {!bluetooth.mojom.DeviceInfo} */
-    this.deviceInfo = deviceInfo;
+      /** @type {!bluetooth.mojom.DeviceInfo} */
+      this.deviceInfo = deviceInfo;
 
-    /** @type {?Array<bluetooth.mojom.ServiceInfo>} */
-    this.services = null;
+      /** @type {?Array<bluetooth.mojom.ServiceInfo>} */
+      this.services = null;
 
-    /** @private {?bluetooth.mojom.DeviceProxy} */
-    this.deviceProxy_ = null;
+      /** @private {?bluetooth.mojom.DeviceProxy} */
+      this.deviceProxy_ = null;
 
-    /** @private {!object_fieldset.ObjectFieldSet} */
-    this.deviceFieldSet_ = new object_fieldset.ObjectFieldSet();
-    this.deviceFieldSet_.setPropertyDisplayNames(PROPERTY_NAMES);
+      /** @private {!object_fieldset.ObjectFieldSet} */
+      this.deviceFieldSet_ = new object_fieldset.ObjectFieldSet();
+      this.deviceFieldSet_.setPropertyDisplayNames(PROPERTY_NAMES);
 
-    /** @private {!service_list.ServiceList} */
-    this.serviceList_ = new service_list.ServiceList();
+      /** @private {!service_list.ServiceList} */
+      this.serviceList_ = new service_list.ServiceList();
 
-    /** @private {!device_collection.ConnectionStatus} */
-    this.status_ = device_collection.ConnectionStatus.DISCONNECTED;
+      /** @private {!device_collection.ConnectionStatus} */
+      this.status_ = device_collection.ConnectionStatus.DISCONNECTED;
 
-    /** @private {?Element} */
-    this.connectBtn_ = null;
+      /** @private {?Element} */
+      this.connectBtn_ = null;
 
-    this.pageDiv.appendChild(document.importNode(
-        $('device-details-template').content, true /* deep */));
+      this.pageDiv.appendChild(document.importNode(
+          $('device-details-template').content, true /* deep */));
 
-    this.pageDiv.querySelector('.device-details')
-        .appendChild(this.deviceFieldSet_);
-    this.pageDiv.querySelector('.services').appendChild(this.serviceList_);
+      this.pageDiv.querySelector('.device-details')
+          .appendChild(this.deviceFieldSet_);
+      this.pageDiv.querySelector('.services').appendChild(this.serviceList_);
 
-    this.pageDiv.querySelector('.forget').addEventListener('click', function() {
-      this.disconnect();
-      this.pageDiv.dispatchEvent(new CustomEvent('forgetpressed', {
-        detail: {
-          address: this.deviceInfo.address,
-        },
-      }));
-    }.bind(this));
+      this.pageDiv.querySelector('.forget').addEventListener(
+          'click', function() {
+            this.disconnect();
+            this.pageDiv.dispatchEvent(new CustomEvent('forgetpressed', {
+              detail: {
+                address: this.deviceInfo.address,
+              },
+            }));
+          }.bind(this));
 
-    this.connectBtn_ = this.pageDiv.querySelector('.disconnect');
-    this.connectBtn_.addEventListener('click', function() {
-      this.deviceProxy_ !== null ? this.disconnect() : this.connect();
-    }.bind(this));
+      this.connectBtn_ = this.pageDiv.querySelector('.disconnect');
+      this.connectBtn_.addEventListener('click', function() {
+        this.deviceProxy_ !== null ? this.disconnect() : this.connect();
+      }.bind(this));
 
-    this.redraw();
-  }
-
-  DeviceDetailsPage.prototype = {
-    __proto__: Page.prototype,
+      this.redraw();
+    }
 
     /** Creates a connection to the Bluetooth device. */
-    connect: function() {
+    connect() {
       if (this.status_ !== device_collection.ConnectionStatus.DISCONNECTED) {
         return;
       }
@@ -128,10 +127,10 @@
             this.updateConnectionStatus_(
                 device_collection.ConnectionStatus.DISCONNECTED);
           }.bind(this));
-    },
+    }
 
     /** Disconnects the page from the Bluetooth device. */
-    disconnect: function() {
+    disconnect() {
       if (!this.deviceProxy_) {
         return;
       }
@@ -140,10 +139,10 @@
       this.deviceProxy_ = null;
       this.updateConnectionStatus_(
           device_collection.ConnectionStatus.DISCONNECTED);
-    },
+    }
 
     /** Redraws the contents of the page with the current |deviceInfo|. */
-    redraw: function() {
+    redraw() {
       var isConnected = this.deviceInfo.isGattConnected;
 
       // Update status if connection has changed.
@@ -178,29 +177,29 @@
 
       this.deviceFieldSet_.setObject(deviceViewObj);
       this.serviceList_.redraw();
-    },
+    }
 
     /**
      * Sets the page's device info and forces a redraw.
      * @param {!bluetooth.mojom.DeviceInfo} info
      */
-    setDeviceInfo: function(info) {
+    setDeviceInfo(info) {
       this.deviceInfo = info;
       this.redraw();
-    },
+    }
 
     /**
      * Fires an 'infochanged' event with the current |deviceInfo|
      * @private
      */
-    fireDeviceInfoChanged_: function() {
+    fireDeviceInfoChanged_() {
       this.pageDiv.dispatchEvent(new CustomEvent('infochanged', {
         bubbles: true,
         detail: {
           info: this.deviceInfo,
         },
       }));
-    },
+    }
 
     /**
      * Updates the current connection status. Caches the latest status, updates
@@ -209,7 +208,7 @@
      * @param {!device_collection.ConnectionStatus} status
      * @private
      */
-    updateConnectionStatus_: function(status) {
+    updateConnectionStatus_(status) {
       if (this.status_ === status) {
         return;
       }
@@ -233,8 +232,8 @@
           status: status,
         }
       }));
-    },
-  };
+    }
+  }
 
   return {
     DeviceDetailsPage: DeviceDetailsPage,
diff --git a/chrome/browser/resources/bluetooth_internals/devices_page.js b/chrome/browser/resources/bluetooth_internals/devices_page.js
index 3c62fd7..af4f1b58 100644
--- a/chrome/browser/resources/bluetooth_internals/devices_page.js
+++ b/chrome/browser/resources/bluetooth_internals/devices_page.js
@@ -8,8 +8,6 @@
  */
 
 cr.define('devices_page', function() {
-  /** @const */ var Page = cr.ui.pageManager.Page;
-
   /**
    * Enum of scan status for the devices page.
    * @enum {number}
@@ -24,41 +22,37 @@
 
   /**
    * Page that contains a header and a DevicesView.
-   * @constructor
-   * @extends {cr.ui.pageManager.Page}
    */
-  function DevicesPage() {
-    Page.call(this, 'devices', 'Devices', 'devices');
+  class DevicesPage extends cr.ui.pageManager.Page {
+    constructor() {
+      super('devices', 'Devices', 'devices');
 
-    this.deviceTable = new device_table.DeviceTable();
-    this.pageDiv.appendChild(this.deviceTable);
-    this.scanBtn_ = this.pageDiv.querySelector('#scan-btn');
-    this.scanBtn_.addEventListener('click', function(event) {
-      this.pageDiv.dispatchEvent(new CustomEvent('scanpressed'));
-    }.bind(this));
-  }
-
-  DevicesPage.prototype = {
-    __proto__: Page.prototype,
+      this.deviceTable = new device_table.DeviceTable();
+      this.pageDiv.appendChild(this.deviceTable);
+      this.scanBtn_ = this.pageDiv.querySelector('#scan-btn');
+      this.scanBtn_.addEventListener('click', event => {
+        this.pageDiv.dispatchEvent(new CustomEvent('scanpressed'));
+      });
+    }
 
     /**
      * Sets the device collection for the page's device table.
      * @param {!device_collection.DeviceCollection} devices
      */
-    setDevices: function(devices) {
+    setDevices(devices) {
       this.deviceTable.setDevices(devices);
-    },
+    }
 
     /**
      * Updates the inspect status of the given |deviceInfo| in the device table.
      * @param {!bluetooth.mojom.DeviceInfo} deviceInfo
      * @param {boolean} isInspecting
      */
-    setInspecting: function(deviceInfo, isInspecting) {
+    setInspecting(deviceInfo, isInspecting) {
       this.deviceTable.setInspecting(deviceInfo, isInspecting);
-    },
+    }
 
-    setScanStatus: function(status) {
+    setScanStatus(status) {
       switch (status) {
         case ScanStatus.OFF:
           this.scanBtn_.disabled = false;
@@ -78,7 +72,7 @@
           break;
       }
     }
-  };
+  }
 
   return {
     DevicesPage: DevicesPage,
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
index 8b940aa..65ce2c9 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
@@ -106,21 +106,19 @@
           disabled="[[!prefs.settings.a11y.screen_magnifier.value]]">
       </settings-dropdown-menu>
     </div>
-    <template is="dom-if" if="[[dockedMagnifierFeatureEnabled_]]" restamp>
-      <settings-toggle-button
-          pref="{{prefs.ash.docked_magnifier.enabled}}"
-          label="$i18n{dockedMagnifierLabel}"
-          disabled="[[prefs.settings.a11y.screen_magnifier.value]]">
-      </settings-toggle-button>
-      <div class="settings-box continuation">
-        <div class="start sub-item">$i18n{dockedMagnifierZoomLabel}</div>
-        <settings-dropdown-menu label="$i18n{dockedMagnifierZoomLabel}"
-            pref="{{prefs.ash.docked_magnifier.scale}}"
-            menu-options="[[screenMagnifierZoomOptions_]]"
-            disabled="[[!prefs.ash.docked_magnifier.enabled.value]]">
-        </settings-dropdown-menu>
-      </div>
-    </template>
+    <settings-toggle-button
+        pref="{{prefs.ash.docked_magnifier.enabled}}"
+        label="$i18n{dockedMagnifierLabel}"
+        disabled="[[prefs.settings.a11y.screen_magnifier.value]]">
+    </settings-toggle-button>
+    <div class="settings-box continuation">
+      <div class="start sub-item">$i18n{dockedMagnifierZoomLabel}</div>
+      <settings-dropdown-menu label="$i18n{dockedMagnifierZoomLabel}"
+          pref="{{prefs.ash.docked_magnifier.scale}}"
+          menu-options="[[screenMagnifierZoomOptions_]]"
+          disabled="[[!prefs.ash.docked_magnifier.enabled.value]]">
+      </settings-dropdown-menu>
+    </div>
     <cr-link-row
         class="hr"
         embedded
diff --git a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
index 571e5d7..2e55dbb 100644
--- a/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
@@ -163,17 +163,6 @@
       },
     },
 
-    /**
-     * Whether the docked magnifier flag is enabled.
-     * @private {boolean}
-     */
-    dockedMagnifierFeatureEnabled_: {
-      type: Boolean,
-      value: function() {
-        return loadTimeData.getBoolean('dockedMagnifierFeatureEnabled');
-      },
-    },
-
     /** @private */
     isGuest_: {
       type: Boolean,
diff --git a/chrome/browser/resources/settings/device_page/display.html b/chrome/browser/resources/settings/device_page/display.html
index c54a4c4..b8c0fc1 100644
--- a/chrome/browser/resources/settings/device_page/display.html
+++ b/chrome/browser/resources/settings/device_page/display.html
@@ -237,61 +237,59 @@
     </div>
 
     <!-- Night Light Settings -->
-    <template is="dom-if" if="[[nightLightFeatureEnabled_]]" restamp>
-      <settings-toggle-button
-          id="nightLightToggleButton"
-          label="$i18n{displayNightLightLabel}"
-          pref="{{prefs.ash.night_light.enabled}}"
-          sub-label="$i18n{displayNightLightText}">
-      </settings-toggle-button>
-      <div id="nightLightSettingsDiv"
-          class="settings-box continuation start layout vertical">
-        <!-- Color temperature slider -->
-        <div id="nightLightTemperatureDiv"
-            class="settings-box indented continuation"
+    <settings-toggle-button
+        id="nightLightToggleButton"
+        label="$i18n{displayNightLightLabel}"
+        pref="{{prefs.ash.night_light.enabled}}"
+        sub-label="$i18n{displayNightLightText}">
+    </settings-toggle-button>
+    <div id="nightLightSettingsDiv"
+        class="settings-box continuation start layout vertical">
+      <!-- Color temperature slider -->
+      <div id="nightLightTemperatureDiv"
+          class="settings-box indented continuation"
+          disabled$="[[!prefs.ash.night_light.enabled.value]]">
+        <div class="start text-area" id="colorTemperatureLabel">
+          $i18n{displayNightLightTemperatureLabel}
+        </div>
+        <settings-slider id="colorTemperatureSlider"
+            aria-labelledby="colorTemperatureLabel" min="0" max="100"
+            scale="100" label-min="$i18n{displayNightLightTempSliderMinLabel}"
+            label-max="$i18n{displayNightLightTempSliderMaxLabel}"
+            pref="{{prefs.ash.night_light.color_temperature}}"
             disabled$="[[!prefs.ash.night_light.enabled.value]]">
-          <div class="start text-area" id="colorTemperatureLabel">
-            $i18n{displayNightLightTemperatureLabel}
-          </div>
-          <settings-slider id="colorTemperatureSlider"
-              aria-labelledby="colorTemperatureLabel" min="0" max="100"
-              scale="100" label-min="$i18n{displayNightLightTempSliderMinLabel}"
-              label-max="$i18n{displayNightLightTempSliderMaxLabel}"
-              pref="{{prefs.ash.night_light.color_temperature}}"
-              disabled$="[[!prefs.ash.night_light.enabled.value]]">
-          </settings-slider>
-        </div>
-        <!-- Schedule settings -->
-        <div class="settings-box indented">
-          <div class="start text-area">
-            <div id="nightLightScheduleLabel" class="label">
-              $i18n{displayNightLightScheduleLabel}
-            </div>
-            <div id="nightLightScheduleSubLabel" class="secondary label"
-                hidden$="[[!nightLightScheduleSubLabel_]]">
-              [[nightLightScheduleSubLabel_]]
-            </div>
-          </div>
-          <settings-dropdown-menu
-              id="nightLightScheduleTypeDropDown"
-              aria-labelledby="nightLightScheduleLabel"
-              pref="{{prefs.ash.night_light.schedule_type}}"
-              menu-options="[[scheduleTypesList_]]">
-          </settings-dropdown-menu>
-        </div>
-        <!-- Custom schedule slider -->
-        <div class="settings-box indented continuation">
-          <iron-collapse id="nightLightCustomScheduleCollapse"
-              class="start text-area layout vertical"
-              opened="[[shouldOpenCustomScheduleCollapse_]]">
-            <div class="settings-box continuation self-stretch">
-              <night-light-slider id="nightLightSlider" prefs="{{prefs}}">
-              </night-light-slider>
-            </div>
-          </iron-collapse>
-        </div>
+        </settings-slider>
       </div>
-    </template>
+      <!-- Schedule settings -->
+      <div class="settings-box indented">
+        <div class="start text-area">
+          <div id="nightLightScheduleLabel" class="label">
+            $i18n{displayNightLightScheduleLabel}
+          </div>
+          <div id="nightLightScheduleSubLabel" class="secondary label"
+              hidden$="[[!nightLightScheduleSubLabel_]]">
+            [[nightLightScheduleSubLabel_]]
+          </div>
+        </div>
+        <settings-dropdown-menu
+            id="nightLightScheduleTypeDropDown"
+            aria-labelledby="nightLightScheduleLabel"
+            pref="{{prefs.ash.night_light.schedule_type}}"
+            menu-options="[[scheduleTypesList_]]">
+        </settings-dropdown-menu>
+      </div>
+      <!-- Custom schedule slider -->
+      <div class="settings-box indented continuation">
+        <iron-collapse id="nightLightCustomScheduleCollapse"
+            class="start text-area layout vertical"
+            opened="[[shouldOpenCustomScheduleCollapse_]]">
+          <div class="settings-box continuation self-stretch">
+            <night-light-slider id="nightLightSlider" prefs="{{prefs}}">
+            </night-light-slider>
+          </div>
+        </iron-collapse>
+      </div>
+    </div>
 
   </template>
   <script src="display.js"></script>
diff --git a/chrome/browser/resources/settings/device_page/display.js b/chrome/browser/resources/settings/device_page/display.js
index 72e2811..c1881cb5 100644
--- a/chrome/browser/resources/settings/device_page/display.js
+++ b/chrome/browser/resources/settings/device_page/display.js
@@ -144,14 +144,6 @@
     },
 
     /** @private */
-    nightLightFeatureEnabled_: {
-      type: Boolean,
-      value: function() {
-        return loadTimeData.getBoolean('nightLightFeatureEnabled');
-      }
-    },
-
-    /** @private */
     unifiedDesktopMode_: {
       type: Boolean,
       value: false,
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
index 6389e71..d212423 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
@@ -15,9 +15,6 @@
 
     /** Retrain the Assistant voice model. */
     retrainAssistantVoiceModel() {}
-
-    /** Delete the Assistant voice model. */
-    deleteAssistantVoiceModel() {}
   }
 
   /** @implements {settings.GoogleAssistantBrowserProxy} */
@@ -31,11 +28,6 @@
     retrainAssistantVoiceModel() {
       chrome.send('retrainAssistantVoiceModel');
     }
-
-    /** @override */
-    deleteAssistantVoiceModel() {
-      chrome.send('deleteAssistantVoiceModel');
-    }
   }
 
   // The singleton instance_ is replaced with a test version of this wrapper
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
index 2a44cd9a..a25cf48 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
@@ -73,10 +73,6 @@
                 on-click="onRetrainVoiceModelTapped_">
               $i18n{googleAssistantVoiceSettingsRetrainButton}
             </paper-button>
-            <paper-button id="button" class="secondary-button"
-                on-click="onDeleteVoiceModelTapped_">
-              $i18n{googleAssistantVoiceSettingsDeleteButton}
-            </paper-button>
           </div>
         </template>
         <settings-toggle-button id="googleAssistantNotificationEnable"
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
index 8205b12..76c45a0 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
@@ -113,11 +113,6 @@
   },
 
   /** @private */
-  onDeleteVoiceModelTapped_: function() {
-    this.browserProxy_.deleteAssistantVoiceModel();
-  },
-
-  /** @private */
   onDspHotwordStateChange_: function() {
     switch (Number(this.$$('#dspHotwordState').value)) {
       case DspHotwordState.DEFAULT_ON:
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 0611bfe..23119518 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -19,18 +19,12 @@
 <link rel="import" href="../site_settings/all_sites.html">
 <link rel="import" href="../site_settings/category_default_setting.html">
 <link rel="import" href="../site_settings/category_setting_exceptions.html">
-<!--
-  In order for the browser test for chooser-exception-list to work
-  properly, the custom element needs to be a dependency of the settings page.
-  TODO(https://crbug.com/854329): Delete this comment once this element is used.
--->
 <link rel="import" href="../site_settings/chooser_exception_list.html">
 <link rel="import" href="../site_settings/constants.html">
 <link rel="import" href="../site_settings/media_picker.html">
 <link rel="import" href="../site_settings/pdf_documents.html">
 <link rel="import" href="../site_settings/protocol_handlers.html">
 <link rel="import" href="../site_settings/site_data_details_subpage.html">
-<link rel="import" href="../site_settings/usb_devices.html">
 <link rel="import" href="../site_settings/site_data.html">
 <link rel="import" href="../site_settings/site_details.html">
 <link rel="import" href="../site_settings/zoom_levels.html">
@@ -553,7 +547,10 @@
                   "$i18n{siteSettingsUsbDevicesAskRecommended}"
               category="{{ContentSettingsTypes.USB_DEVICES}}">
           </category-default-setting>
-          <usb-devices></usb-devices>
+          <chooser-exception-list
+              category="{{ContentSettingsTypes.USB_DEVICES}}"
+              chooser-type="{{ChooserType.USB_DEVICES}}">
+          </chooser-exception-list>
         </settings-subpage>
       </template>
       <template is="dom-if" route-path="/content/siteDetails" no-search>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chrome/browser/resources/settings/privacy_page/privacy_page.js
index 62a2ad72..e379410 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -177,6 +177,7 @@
   /** @override */
   ready: function() {
     this.ContentSettingsTypes = settings.ContentSettingsTypes;
+    this.ChooserType = settings.ChooserType;
 
     this.browserProxy_ = settings.PrivacyPageBrowserProxyImpl.getInstance();
 
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 03fa53f..058d8853 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1190,12 +1190,6 @@
                  preprocess="true"
                  file="lazy_load.html"
                  type="chrome_html" />
-      <structure name="IDR_SETTINGS_USB_DEVICES_HTML"
-                 file="site_settings/usb_devices.html"
-                 type="chrome_html" />
-      <structure name="IDR_SETTINGS_USB_DEVICES_JS"
-                 file="site_settings/usb_devices.js"
-                 type="chrome_html" />
       <structure name="IDR_SETTINGS_WEBSITE_USAGE_PRIVATE_API_HTML"
                  file="site_settings/website_usage_private_api.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/site_settings/BUILD.gn b/chrome/browser/resources/settings/site_settings/BUILD.gn
index e4077b8..527f2ce 100644
--- a/chrome/browser/resources/settings/site_settings/BUILD.gn
+++ b/chrome/browser/resources/settings/site_settings/BUILD.gn
@@ -27,7 +27,6 @@
     ":site_list_entry",
     ":site_settings_behavior",
     ":site_settings_prefs_browser_proxy",
-    ":usb_devices",
     ":website_usage_private_api",
     ":zoom_levels",
   ]
@@ -138,14 +137,6 @@
   ]
 }
 
-js_library("usb_devices") {
-  deps = [
-    ":site_settings_behavior",
-    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
-  ]
-}
-
 js_library("site_data") {
   deps = [
     ":cookie_info",
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index b5ed736..4375947da 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -114,24 +114,6 @@
 let ProtocolHandlerEntry;
 
 /**
- * @typedef {{name: string,
- *            product-id: Number,
- *            serial-number: string,
- *            vendor-id: Number}}
- */
-let UsbDeviceDetails;
-
-/**
- * @typedef {{embeddingOrigin: string,
- *            object: UsbDeviceDetails,
- *            objectName: string,
- *            origin: string,
- *            setting: string,
- *            source: string}}
- */
-let UsbDeviceEntry;
-
-/**
  * @typedef {{origin: string,
  *            setting: string,
  *            source: string,
@@ -331,22 +313,6 @@
     removeProtocolHandler(protocol, url) {}
 
     /**
-     * Fetches a list of all USB devices and the sites permitted to use them.
-     * @return {!Promise<!Array<!UsbDeviceEntry>>} The list of USB devices.
-     */
-    fetchUsbDevices() {}
-
-    /**
-     * Removes a particular USB device object permission by origin and embedding
-     * origin.
-     * @param {string} origin The origin to look up the permission for.
-     * @param {string} embeddingOrigin the embedding origin to look up.
-     * @param {!UsbDeviceDetails} usbDevice The USB device to revoke permission
-     *     for.
-     */
-    removeUsbDevice(origin, embeddingOrigin, usbDevice) {}
-
-    /**
      * Fetches the incognito status of the current profile (whether an incognito
      * profile exists). Returns the results via onIncognitoStatusChanged.
      */
@@ -508,16 +474,6 @@
     }
 
     /** @override */
-    fetchUsbDevices() {
-      return cr.sendWithPromise('fetchUsbDevices');
-    }
-
-    /** @override */
-    removeUsbDevice(origin, embeddingOrigin, usbDevice) {
-      chrome.send('removeUsbDevice', [origin, embeddingOrigin, usbDevice]);
-    }
-
-    /** @override */
     updateIncognitoStatus() {
       chrome.send('updateIncognitoStatus');
     }
diff --git a/chrome/browser/resources/settings/site_settings/usb_devices.html b/chrome/browser/resources/settings/site_settings/usb_devices.html
deleted file mode 100644
index 27a4f9c4..0000000
--- a/chrome/browser/resources/settings/site_settings/usb_devices.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
-
-<link rel="import" href="../i18n_setup.html">
-<link rel="import" href="../settings_shared_css.html">
-<link rel="import" href="../site_favicon.html">
-<link rel="import" href="site_settings_behavior.html">
-<link rel="import" href="site_settings_prefs_browser_proxy.html">
-
-<dom-module id="usb-devices">
-  <template>
-    <style include="settings-shared">
-      :host {
-        display: block;
-      }
-
-      .column-header {
-        margin-bottom: 15px;
-        margin-inline-start: 20px;
-        margin-top: 15px;
-      }
-    </style>
-
-    <div class="settings-box first" hidden$="[[hasDevices_(devices_)]]">
-      $i18n{noUsbDevicesFound}
-    </div>
-    <template is="dom-repeat" items="[[devices_]]">
-      <div class="column-header">[[item.objectName]]</div>
-
-      <div class="list-frame menu-content vertical-list">
-        <div class="list-item">
-          <site-favicon url="[[item.origin]]"></site-favicon>
-          <div class="middle">
-            <span class="url-directionality">[[item.origin]]</span>
-          </div>
-
-          <paper-icon-button-light class="icon-more-vert">
-            <button on-click="showMenu_" title="$i18n{moreActions}"></button>
-          </paper-icon-button-light>
-        </div>
-      </div>
-    </template>
-
-    <cr-action-menu>
-      <button id="removeButton" class="dropdown-item"
-          on-click="onRemoveTap_">
-        $i18n{handlerRemove}
-      </button>
-    </cr-action-menu>
-
-  </template>
-  <script src="usb_devices.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/site_settings/usb_devices.js b/chrome/browser/resources/settings/site_settings/usb_devices.js
deleted file mode 100644
index 46a5498d..0000000
--- a/chrome/browser/resources/settings/site_settings/usb_devices.js
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview
- * 'usb-devices' is the polymer element for showing the USB Devices category
- * under Site Settings.
- */
-
-Polymer({
-  is: 'usb-devices',
-
-  behaviors: [SiteSettingsBehavior],
-
-  properties: {
-    /**
-     * A list of all USB devices.
-     * @private {!Array<!UsbDeviceEntry>}
-     */
-    devices_: Array,
-
-    /**
-     * The targeted object for menu operations.
-     * @private {?Object}
-     */
-    actionMenuModel_: Object
-  },
-
-  /** @override */
-  ready: function() {
-    this.fetchUsbDevices_();
-  },
-
-  /**
-   * Fetch the list of USB devices and update the list.
-   * @private
-   */
-  fetchUsbDevices_: function() {
-    this.browserProxy.fetchUsbDevices().then(deviceList => {
-      this.devices_ = deviceList;
-    });
-  },
-
-  /**
-   * @return {boolean}
-   * @private
-   */
-  hasDevices_: function() {
-    return !!(this.devices_ && this.devices_.length);
-  },
-
-  /**
-   * A handler when an action is selected in the action menu.
-   * @private
-   */
-  onRemoveTap_: function() {
-    this.$$('cr-action-menu').close();
-
-    const item = this.actionMenuModel_;
-    this.browserProxy.removeUsbDevice(
-        item.origin, item.embeddingOrigin, item.object);
-    this.actionMenuModel_ = null;
-    this.fetchUsbDevices_();
-  },
-
-  /**
-   * A handler to show the action menu next to the clicked menu button.
-   * @param {!{model: !{item: UsbDeviceEntry}}} event
-   * @private
-   */
-  showMenu_: function(event) {
-    this.actionMenuModel_ = event.model.item;
-    /** @type {!CrActionMenuElement} */ (this.$$('cr-action-menu'))
-        .showAt(
-            /** @type {!Element} */ (
-                Polymer.dom(/** @type {!Event} */ (event)).localTarget));
-  }
-});
diff --git a/chrome/browser/resources/welcome/welcome.html b/chrome/browser/resources/welcome/welcome.html
index 9840052..a1e54f0 100644
--- a/chrome/browser/resources/welcome/welcome.html
+++ b/chrome/browser/resources/welcome/welcome.html
@@ -128,7 +128,7 @@
 
         .subheading {
           animation: fadeInAndSlideUp 600ms 1.9s cubic-bezier(.4, .2, 0, 1) both;
-          color: #939393;
+          color: #5f6368;
           font-size: 1em;
           font-weight: 500;
           margin-top: .25em;
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index 39785c8..4126eaa5 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//extensions/buildflags/buildflags.gni")
-import("//third_party/protobuf/proto_library.gni")
 
 static_library("safe_browsing") {
   sources = [
@@ -298,6 +297,7 @@
     ]
 
     deps = [
+      ":safe_browsing",
       "//chrome/common/safe_browsing:proto",
       "//content/public/browser:browser",
     ]
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 03d417f..85da0e38 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -93,17 +93,6 @@
                      chrome::kChromeSearchLocalNtpBackgroundFilename));
 }
 
-// In some cases (Sync, upgrading versions) its necessary to check if the file
-// actually exists and is in the correct location.
-bool CheckLocalBackgroundImageExists(const base::FilePath& profile_path) {
-  base::FilePath user_data_dir;
-  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-  base::FilePath profile_image =
-      profile_path.AppendASCII(chrome::kChromeSearchLocalNtpBackgroundFilename);
-
-  return base::PathExists(profile_image);
-}
-
 void DoDeleteThumbnailDataIfExists(
     const base::FilePath& database_dir,
     base::Optional<base::OnceCallback<void(bool)>> callback) {
@@ -116,12 +105,6 @@
     std::move(*callback).Run(result);
 }
 
-bool IsLocalFileUrl(GURL url) {
-  return base::StartsWith(url.spec(),
-                          chrome::kChromeSearchLocalNtpBackgroundUrl,
-                          base::CompareCase::SENSITIVE);
-}
-
 }  // namespace
 
 // Keeps track of any changes in search engine provider and notifies
@@ -238,7 +221,7 @@
   pref_change_registrar_.Init(pref_service_);
   pref_change_registrar_.Add(
       prefs::kNtpCustomBackgroundDict,
-      base::BindRepeating(&InstantService::UpdateThemeInfo,
+      base::BindRepeating(&InstantService::UpdateBackgroundFromSync,
                           base::Unretained(this)));
 }
 
@@ -348,6 +331,12 @@
   NotifyAboutThemeInfo();
 }
 
+void InstantService::UpdateBackgroundFromSync() {
+  // Any incoming change to synced background data should clear the local image.
+  pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false);
+  UpdateThemeInfo();
+}
+
 void InstantService::UpdateMostVisitedItemsInfo() {
   NotifyAboutMostVisitedItems();
 }
@@ -375,6 +364,10 @@
       background_service_ &&
       background_service_->IsValidBackdropUrl(background_url);
 
+  bool need_forced_refresh =
+      pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice) &&
+      pref_service_->FindPreference(prefs::kNtpCustomBackgroundDict)
+          ->IsDefaultValue();
   pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false);
   RemoveLocalBackgroundImageCopy();
 
@@ -384,20 +377,19 @@
     pref_service_->Set(prefs::kNtpCustomBackgroundDict, background_info);
   } else {
     pref_service_->ClearPref(prefs::kNtpCustomBackgroundDict);
+
+    // If this device was using a local image and did not have a non-local
+    // background saved, UpdateBackgroundFromSync will not fire. Therefore, we
+    // need to force a refresh here.
+    if (need_forced_refresh) {
+      UpdateThemeInfo();
+    }
   }
 }
 
 void InstantService::SetBackgroundToLocalResource() {
-  // Add a timestamp to the url to prevent the browser from using a cached
-  // version when "Upload an image" is used multiple times.
-  std::string time_string = std::to_string(base::Time::Now().ToTimeT());
-  std::string local_string(chrome::kChromeSearchLocalNtpBackgroundUrl);
-  GURL timestamped_url(local_string + "?ts=" + time_string);
-
-  base::DictionaryValue background_info = GetBackgroundInfoAsDict(
-      timestamped_url, std::string(), std::string(), GURL());
   pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, true);
-  pref_service_->Set(prefs::kNtpCustomBackgroundDict, background_info);
+  UpdateThemeInfo();
 }
 
 void InstantService::SelectLocalBackgroundImage(const base::FilePath& path) {
@@ -615,6 +607,16 @@
     return;
   }
 
+  if (pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice)) {
+    // Add a timestamp to the url to prevent the browser from using a cached
+    // version when "Upload an image" is used multiple times.
+    std::string time_string = std::to_string(base::Time::Now().ToTimeT());
+    std::string local_string(chrome::kChromeSearchLocalNtpBackgroundUrl);
+    GURL timestamped_url(local_string + "?ts=" + time_string);
+    theme_info_->custom_background_url = timestamped_url;
+    return;
+  }
+
   // Attempt to get custom background URL from preferences.
   GURL custom_background_url;
   if (!IsCustomBackgroundPrefValid(custom_background_url)) {
@@ -622,50 +624,15 @@
     return;
   }
 
-  // If the url points to a local image check that it actually exists, if not
-  // fall back to the default background.
-  const PrefService::Preference* background_local_to_device_pref =
-      pref_service_->FindPreference(prefs::kNtpCustomBackgroundLocalToDevice);
-  if (IsLocalFileUrl(custom_background_url) &&
-      background_local_to_device_pref->IsDefaultValue()) {
-    base::PostTaskWithTraitsAndReplyWithResult(
-        FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-        base::BindOnce(&CheckLocalBackgroundImageExists, profile_->GetPath()),
-        base::BindOnce(
-            &InstantService::ApplyCustomBackgroundThemeInfoFromLocalFile,
-            weak_ptr_factory_.GetWeakPtr()));
-    return;
-  }
   ApplyCustomBackgroundThemeInfo();
 }
 
-void InstantService::ApplyCustomBackgroundThemeInfoFromLocalFile(
-    bool file_exists) {
-  pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice,
-                            file_exists);
-
-  ApplyCustomBackgroundThemeInfo();
-
-  // This method executes as a result of PostTask... so the call to
-  // NotifyAboutThemeInfo from UpdateThemeInfo is lost, explicitly send it
-  // here instead.
-  NotifyAboutThemeInfo();
-}
-
 void InstantService::ApplyCustomBackgroundThemeInfo() {
   const base::DictionaryValue* background_info =
       pref_service_->GetDictionary(prefs::kNtpCustomBackgroundDict);
   GURL custom_background_url(
       background_info->FindKey(kNtpCustomBackgroundURL)->GetString());
 
-  // If a background was set using a local image on a different device fallback
-  // to the default background.
-  if (IsLocalFileUrl(custom_background_url) &&
-      !pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice)) {
-    FallbackToDefaultThemeInfo();
-    return;
-  }
-
   // Set custom background information in theme info (attributions are
   // optional).
   const base::Value* attribution_line_1 =
@@ -715,15 +682,13 @@
 }
 
 bool InstantService::IsCustomBackgroundSet() {
+  if (pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice))
+    return true;
+
   GURL custom_background_url;
   if (!IsCustomBackgroundPrefValid(custom_background_url))
     return false;
 
-  if (IsLocalFileUrl(custom_background_url) &&
-      !pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice)) {
-    return false;
-  }
-
   return true;
 }
 
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index ca2ad8f0..4c5b1226 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -108,6 +108,10 @@
   // necessary. Investigate more and remove this from here.
   void UpdateThemeInfo();
 
+  // Invoked when a background pref update is received via sync, triggering
+  // an update of theme info.
+  void UpdateBackgroundFromSync();
+
   // Invoked by the InstantController to update most visited items details for
   // NTP.
   void UpdateMostVisitedItemsInfo();
@@ -175,7 +179,6 @@
   void ApplyOrResetCustomBackgroundThemeInfo();
 
   void ApplyCustomBackgroundThemeInfo();
-  void ApplyCustomBackgroundThemeInfoFromLocalFile(bool file_exists);
 
   void ResetCustomBackgroundThemeInfo();
 
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index edfe9ee..a6435c2 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -329,26 +329,7 @@
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 }
 
-TEST_F(InstantServiceTest, NoLocalFileExists) {
-  ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
-  const GURL kUrl("chrome-search://local-ntp/background.jpg?123456789");
-
-  sync_preferences::TestingPrefServiceSyncable* pref_service =
-      profile()->GetTestingPrefService();
-
-  pref_service->SetUserPref(
-      prefs::kNtpCustomBackgroundDict,
-      std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrl)));
-  thread_bundle()->RunUntilIdle();
-
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
-  EXPECT_EQ(GURL(), theme_info->custom_background_url);
-  EXPECT_EQ(false,
-            pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
-  EXPECT_FALSE(instant_service_->IsCustomBackgroundSet());
-}
-
-TEST_F(InstantServiceTest, LocalFileExists) {
+TEST_F(InstantServiceTest, SetLocalImage) {
   ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
   const GURL kUrl("chrome-search://local-ntp/background.jpg?123456789");
 
@@ -361,27 +342,38 @@
   base::WriteFile(path, "background_image", 16);
   base::TaskScheduler::GetInstance()->FlushForTesting();
 
-  pref_service->SetUserPref(
-      prefs::kNtpCustomBackgroundDict,
-      std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrl)));
+  instant_service_->SelectLocalBackgroundImage(path);
   thread_bundle()->RunUntilIdle();
 
   ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
-  EXPECT_EQ(kUrl, theme_info->custom_background_url);
-  EXPECT_EQ(true,
-            pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
+  EXPECT_TRUE(base::StartsWith(theme_info->custom_background_url.spec(),
+                               chrome::kChromeSearchLocalNtpBackgroundUrl,
+                               base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(
+      pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 }
 
-TEST_F(InstantServiceTest, LocalFilePrefSet) {
+TEST_F(InstantServiceTest, SyncPrefOverridesLocalImage) {
   ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
-  const GURL kUrl("chrome-search://local-ntp/background.jpg?123456789");
+  const GURL kUrl("https://www.foo.com/");
 
   sync_preferences::TestingPrefServiceSyncable* pref_service =
       profile()->GetTestingPrefService();
 
-  pref_service->SetUserPref(prefs::kNtpCustomBackgroundLocalToDevice,
-                            std::make_unique<base::Value>(true));
+  base::FilePath profile_path = profile()->GetPath();
+  base::FilePath path(profile_path.AppendASCII(
+      chrome::kChromeSearchLocalNtpBackgroundFilename));
+  base::WriteFile(path, "background_image", 16);
+  base::TaskScheduler::GetInstance()->FlushForTesting();
+
+  instant_service_->SelectLocalBackgroundImage(path);
+  thread_bundle()->RunUntilIdle();
+
+  EXPECT_TRUE(
+      pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
+
+  // Update theme info via Sync.
   pref_service->SetUserPref(
       prefs::kNtpCustomBackgroundDict,
       std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrl)));
@@ -389,6 +381,8 @@
 
   ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
+  EXPECT_FALSE(
+      pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 }
 
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
index a1db574..2744ce2 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -391,6 +391,12 @@
         event->GetInteger(constants::kCharIndexKey, &char_index));
   }
 
+  int length = -1;
+  if (event->HasKey(constants::kLengthKey)) {
+    EXTENSION_FUNCTION_VALIDATE(
+        event->GetInteger(constants::kLengthKey, &length));
+  }
+
   // Make sure the extension has included this event type in its manifest.
   bool event_type_allowed = false;
   Profile* profile = Profile::FromBrowserContext(browser_context());
@@ -411,30 +417,30 @@
   content::TtsController* controller = content::TtsController::GetInstance();
   if (event_type == constants::kEventTypeStart) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_START, char_index,
-                           std::string());
+                           length, std::string());
   } else if (event_type == constants::kEventTypeEnd) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_END, char_index,
-                           std::string());
+                           length, std::string());
   } else if (event_type == constants::kEventTypeWord) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_WORD, char_index,
-                           std::string());
+                           length, std::string());
   } else if (event_type == constants::kEventTypeSentence) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_SENTENCE,
-                           char_index, std::string());
+                           char_index, length, std::string());
   } else if (event_type == constants::kEventTypeMarker) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_MARKER, char_index,
-                           std::string());
+                           length, std::string());
   } else if (event_type == constants::kEventTypeError) {
     std::string error_message;
     event->GetString(constants::kErrorMessageKey, &error_message);
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_ERROR, char_index,
-                           error_message);
+                           length, error_message);
   } else if (event_type == constants::kEventTypePause) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_PAUSE, char_index,
-                           std::string());
+                           length, std::string());
   } else if (event_type == constants::kEventTypeResume) {
     controller->OnTtsEvent(utterance_id, content::TTS_EVENT_RESUME, char_index,
-                           std::string());
+                           length, std::string());
   } else {
     EXTENSION_FUNCTION_VALIDATE(false);
   }
diff --git a/chrome/browser/speech/extension_api/tts_extension_api.cc b/chrome/browser/speech/extension_api/tts_extension_api.cc
index a37c3620..04f6b62 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api.cc
@@ -95,6 +95,7 @@
   void OnTtsEvent(content::TtsUtterance* utterance,
                   content::TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override;
 
  private:
@@ -111,6 +112,7 @@
 void TtsExtensionEventHandler::OnTtsEvent(content::TtsUtterance* utterance,
                                           content::TtsEventType event_type,
                                           int char_index,
+                                          int length,
                                           const std::string& error_message) {
   if (utterance->GetSrcId() < 0) {
     if (utterance->IsFinished())
@@ -131,6 +133,8 @@
   std::unique_ptr<base::DictionaryValue> details(new base::DictionaryValue());
   if (char_index >= 0)
     details->SetInteger(constants::kCharIndexKey, char_index);
+  if (length >= 0)
+    details->SetInteger(constants::kLengthKey, length);
   details->SetString(constants::kEventTypeKey, event_type_string);
   if (event_type == content::TTS_EVENT_ERROR) {
     details->SetString(constants::kErrorMessageKey, error_message);
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_constants.cc b/chrome/browser/speech/extension_api/tts_extension_api_constants.cc
index 8002759..4a7a88f 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_constants.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.cc
@@ -7,6 +7,7 @@
 namespace tts_extension_api_constants {
 
 const char kCharIndexKey[] = "charIndex";
+const char kLengthKey[] = "length";
 const char kDesiredEventTypesKey[] = "desiredEventTypes";
 const char kEnqueueKey[] = "enqueue";
 const char kErrorMessageKey[] = "errorMessage";
diff --git a/chrome/browser/speech/extension_api/tts_extension_api_constants.h b/chrome/browser/speech/extension_api/tts_extension_api_constants.h
index 9a0b25eb..a55d377 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_constants.h
+++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.h
@@ -12,6 +12,7 @@
 namespace tts_extension_api_constants {
 
 extern const char kCharIndexKey[];
+extern const char kLengthKey[];
 extern const char kDesiredEventTypesKey[];
 extern const char kEnqueueKey[];
 extern const char kErrorMessageKey[];
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
index 07660ff..168b2aea 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -107,7 +107,7 @@
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
         base::Bind(&MockTtsPlatformImpl::SendEvent, ptr_factory_.GetWeakPtr(),
-                   false, g_saved_utterance_id, content::TTS_EVENT_END, 0,
+                   false, g_saved_utterance_id, content::TTS_EVENT_END, 0, 0,
                    std::string()),
         base::TimeDelta());
   }
@@ -121,7 +121,7 @@
         FROM_HERE,
         base::Bind(&MockTtsPlatformImpl::SendEvent, ptr_factory_.GetWeakPtr(),
                    false, utterance_id, content::TTS_EVENT_END,
-                   utterance.size(), std::string()),
+                   utterance.size(), 0, std::string()),
         base::TimeDelta());
   }
 
@@ -135,7 +135,7 @@
         FROM_HERE,
         base::Bind(&MockTtsPlatformImpl::SendEvent, ptr_factory_.GetWeakPtr(),
                    true, utterance_id, content::TTS_EVENT_END, utterance.size(),
-                   std::string()),
+                   0, std::string()),
         base::TimeDelta());
   }
 
@@ -150,7 +150,7 @@
             FROM_HERE,
             base::Bind(&MockTtsPlatformImpl::SendEvent,
                        ptr_factory_.GetWeakPtr(), false, utterance_id,
-                       content::TTS_EVENT_WORD, i, std::string()),
+                       content::TTS_EVENT_WORD, i, 1, std::string()),
             base::TimeDelta());
       }
     }
@@ -160,6 +160,7 @@
                  int utterance_id,
                  content::TtsEventType event_type,
                  int char_index,
+                 int length,
                  const std::string& message) {
     content::TtsController* tts_controller =
         content::TtsController::GetInstance();
@@ -167,12 +168,14 @@
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE,
           base::Bind(&MockTtsPlatformImpl::SendEvent, ptr_factory_.GetWeakPtr(),
-                     true, utterance_id, event_type, char_index, message),
+                     true, utterance_id, event_type, char_index, length,
+                     message),
           base::TimeDelta::FromMilliseconds(100));
       return;
     }
 
-    tts_controller->OnTtsEvent(utterance_id, event_type, char_index, message);
+    tts_controller->OnTtsEvent(utterance_id, event_type, char_index, length,
+                               message);
   }
 
  private:
diff --git a/chrome/browser/speech/tts_message_filter.cc b/chrome/browser/speech/tts_message_filter.cc
index b1ccc84ef..404cdf0 100644
--- a/chrome/browser/speech/tts_message_filter.cc
+++ b/chrome/browser/speech/tts_message_filter.cc
@@ -151,6 +151,7 @@
 void TtsMessageFilter::OnTtsEvent(content::TtsUtterance* utterance,
                                   content::TtsEventType event_type,
                                   int char_index,
+                                  int length,
                                   const std::string& error_message) {
   if (!Valid())
     return;
@@ -164,6 +165,10 @@
       Send(new TtsMsg_DidFinishSpeaking(utterance->GetSrcId()));
       break;
     case content::TTS_EVENT_WORD:
+      // We do not send the length here because the IPC message
+      // does not expect it. A SpeechSynthesisEvent does not currently
+      // take a length parameter in the TTS event.
+      // TODO(crbug.com/923556) Support length in the SpeechSynthesis API.
       Send(new TtsMsg_WordBoundary(utterance->GetSrcId(), char_index));
       break;
     case content::TTS_EVENT_SENTENCE:
diff --git a/chrome/browser/speech/tts_message_filter.h b/chrome/browser/speech/tts_message_filter.h
index 37c62a4..ffb607f 100644
--- a/chrome/browser/speech/tts_message_filter.h
+++ b/chrome/browser/speech/tts_message_filter.h
@@ -38,6 +38,7 @@
   void OnTtsEvent(content::TtsUtterance* utterance,
                   content::TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override;
 
   // VoicesChangedDelegate implementation.
diff --git a/chrome/browser/supervised_user/supervised_user_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
index 26506f07..4bbd14f 100644
--- a/chrome/browser/supervised_user/supervised_user_service_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_unittest.cc
@@ -442,16 +442,6 @@
  public:
   SupervisedUserServiceExtensionTest()
       : SupervisedUserServiceExtensionTestBase(true) {}
-
- protected:
-  void InitSupervisedUserInitiatedExtensionInstallFeature(bool enabled) {
-    if (enabled) {
-      scoped_feature_list_.InitAndEnableFeature(
-          supervised_users::kSupervisedUserInitiatedExtensionInstall);
-    }
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(SupervisedUserServiceExtensionTest,
@@ -460,9 +450,6 @@
       SupervisedUserServiceFactory::GetForProfile(profile_.get());
   ASSERT_TRUE(profile_->IsSupervised());
 
-  // Disable supervised user initiated installs.
-  InitSupervisedUserInitiatedExtensionInstallFeature(false);
-
   // Check that a supervised user can install and uninstall a theme even if
   // they are not allowed to install extensions.
   {
@@ -510,14 +497,21 @@
 #endif
 }
 
+// TODO(crbug.com/910597): Flaky due to use of ScopedFeatureList.
 TEST_F(SupervisedUserServiceExtensionTest,
-       ExtensionManagementPolicyProviderWithSUInitiatedInstalls) {
+       DISABLED_ExtensionManagementPolicyProviderWithSUInitiatedInstalls) {
+  // Enable supervised user initiated installs.
+  // TODO(crbug.com/846380): ScopedFeatureList must be initialized before the
+  // TestBrowserThreadBundle in ExtensionServiceTestBase to avoid races between
+  // threads.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      supervised_users::kSupervisedUserInitiatedExtensionInstall);
+
   SupervisedUserService* supervised_user_service =
       SupervisedUserServiceFactory::GetForProfile(profile_.get());
   ASSERT_TRUE(profile_->IsSupervised());
 
-  // Enable supervised user initiated installs.
-  InitSupervisedUserInitiatedExtensionInstallFeature(true);
   // The supervised user should be able to load and uninstall the extensions
   // they install.
   {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7ab4785..daf6d6ad 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3719,6 +3719,8 @@
     sources += [
       "ash/fake_tablet_mode_controller.cc",
       "ash/fake_tablet_mode_controller.h",
+      "ash/test_session_controller.cc",
+      "ash/test_session_controller.h",
       "ash/test_wallpaper_controller.cc",
       "ash/test_wallpaper_controller.h",
     ]
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 8e71993e..906f8bf 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -312,11 +312,9 @@
 void ChromeBrowserMainExtraPartsAsh::PostBrowserStart() {
   mobile_data_notifications_ = std::make_unique<MobileDataNotifications>();
 
-  if (ash::features::IsNightLightEnabled()) {
-    night_light_client_ = std::make_unique<NightLightClient>(
-        g_browser_process->shared_url_loader_factory());
-    night_light_client_->Start();
-  }
+  night_light_client_ = std::make_unique<NightLightClient>(
+      g_browser_process->shared_url_loader_factory());
+  night_light_client_->Start();
 }
 
 void ChromeBrowserMainExtraPartsAsh::PostMainMessageLoopRun() {
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index 1afbec5..c779638 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -559,10 +559,6 @@
 }
 
 void SessionControllerClient::ConnectToSessionController() {
-  // Tests may bind to their own SessionController.
-  if (session_controller_)
-    return;
-
   content::ServiceManagerConnection::GetForProcess()
       ->GetConnector()
       ->BindInterface(ash::mojom::kServiceName, &session_controller_);
diff --git a/chrome/browser/ui/ash/session_controller_client_unittest.cc b/chrome/browser/ui/ash/session_controller_client_unittest.cc
index e3d01b19..8a633788 100644
--- a/chrome/browser/ui/ash/session_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/session_controller_client_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/ui/ash/test_session_controller.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -33,6 +34,7 @@
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_service_manager_context.h"
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
@@ -101,78 +103,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestChromeUserManager);
 };
 
-// A session controller interface implementation that tracks sessions and users.
-class TestSessionController : public ash::mojom::SessionController {
- public:
-  TestSessionController() : binding_(this) {}
-  ~TestSessionController() override {}
-
-  ash::mojom::SessionControllerPtr CreateInterfacePtrAndBind() {
-    ash::mojom::SessionControllerPtr ptr;
-    binding_.Bind(mojo::MakeRequest(&ptr));
-    return ptr;
-  }
-
-  ash::mojom::SessionInfo* last_session_info() {
-    return last_session_info_.get();
-  }
-
-  ash::mojom::UserSession* last_user_session() {
-    return last_user_session_.get();
-  }
-
-  int update_user_session_count() { return update_user_session_count_; }
-
-  // ash::mojom::SessionController:
-  void SetClient(ash::mojom::SessionControllerClientPtr client) override {}
-  void SetSessionInfo(ash::mojom::SessionInfoPtr info) override {
-    last_session_info_ = info->Clone();
-  }
-  void UpdateUserSession(ash::mojom::UserSessionPtr user_session) override {
-    last_user_session_ = user_session->Clone();
-    update_user_session_count_++;
-  }
-  void SetUserSessionOrder(
-      const std::vector<uint32_t>& user_session_order) override {}
-  void PrepareForLock(PrepareForLockCallback callback) override {}
-  void StartLock(StartLockCallback callback) override {}
-  void NotifyChromeLockAnimationsComplete() override {}
-  void RunUnlockAnimation(RunUnlockAnimationCallback callback) override {}
-  void NotifyChromeTerminating() override {}
-  void SetSessionLengthLimit(base::TimeDelta length_limit,
-                             base::TimeTicks start_time) override {
-    last_session_length_limit_ = length_limit;
-    last_session_start_time_ = start_time;
-  }
-  void CanSwitchActiveUser(CanSwitchActiveUserCallback callback) override {
-    std::move(callback).Run(true);
-  }
-  void ShowMultiprofilesIntroDialog(
-      ShowMultiprofilesIntroDialogCallback callback) override {
-    std::move(callback).Run(true, false);
-  }
-  void ShowTeleportWarningDialog(
-      ShowTeleportWarningDialogCallback callback) override {
-    std::move(callback).Run(true, false);
-  }
-  void ShowMultiprofilesSessionAbortedDialog(
-      const std::string& user_email) override {}
-  void AddSessionActivationObserverForAccountId(
-      const AccountId& account_id,
-      ash::mojom::SessionActivationObserverPtr observer) override {}
-
-  base::TimeDelta last_session_length_limit_;
-  base::TimeTicks last_session_start_time_;
-
- private:
-  mojo::Binding<ash::mojom::SessionController> binding_;
-
-  ash::mojom::SessionInfoPtr last_session_info_;
-  ash::mojom::UserSessionPtr last_user_session_;
-  int update_user_session_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSessionController);
-};
 
 }  // namespace
 
@@ -261,6 +191,7 @@
   }
 
   content::TestBrowserThreadBundle threads_;
+  content::TestServiceManagerContext context_;
   std::unique_ptr<network::CertVerifierWithTrustAnchors> cert_verifier_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   session_manager::SessionManager session_manager_;
@@ -294,7 +225,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
 
   const AccountId first_user =
@@ -475,7 +405,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
   SessionControllerClient::FlushForTesting();
 
@@ -514,7 +443,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
   SessionControllerClient::FlushForTesting();
 
@@ -577,7 +505,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
 
   const AccountId owner =
@@ -600,7 +527,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
 
   const AccountId owner =
@@ -621,7 +547,6 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
   SessionControllerClient::FlushForTesting();
 
@@ -650,7 +575,6 @@
   user_prefs->SetBoolean(ash::prefs::kAllowScreenLock, false);
   SessionControllerClient::FlushForTesting();
   EXPECT_FALSE(session_controller.last_session_info()->can_lock_screen);
-
   user_prefs->SetBoolean(ash::prefs::kEnableAutoScreenLock, true);
   SessionControllerClient::FlushForTesting();
   EXPECT_TRUE(
@@ -665,13 +589,12 @@
   // Create an object to test and connect it to our test interface.
   SessionControllerClient client;
   TestSessionController session_controller;
-  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
   client.Init();
   SessionControllerClient::FlushForTesting();
 
   // By default there is no session length limit.
-  EXPECT_TRUE(session_controller.last_session_length_limit_.is_zero());
-  EXPECT_TRUE(session_controller.last_session_start_time_.is_null());
+  EXPECT_TRUE(session_controller.last_session_length_limit().is_zero());
+  EXPECT_TRUE(session_controller.last_session_start_time().is_null());
 
   // Setting a session length limit in local state sends it to ash.
   const base::TimeDelta length_limit = base::TimeDelta::FromHours(1);
@@ -681,6 +604,6 @@
                           length_limit.InMilliseconds());
   local_state->SetInt64(prefs::kSessionStartTime, start_time.ToInternalValue());
   SessionControllerClient::FlushForTesting();
-  EXPECT_EQ(length_limit, session_controller.last_session_length_limit_);
-  EXPECT_EQ(start_time, session_controller.last_session_start_time_);
+  EXPECT_EQ(length_limit, session_controller.last_session_length_limit());
+  EXPECT_EQ(start_time, session_controller.last_session_start_time());
 }
diff --git a/chrome/browser/ui/ash/test_session_controller.cc b/chrome/browser/ui/ash/test_session_controller.cc
new file mode 100644
index 0000000..1b2de4a
--- /dev/null
+++ b/chrome/browser/ui/ash/test_session_controller.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/test_session_controller.h"
+
+#include <utility>
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/service_filter.h"
+
+TestSessionController::TestSessionController() {
+  CHECK(content::ServiceManagerConnection::GetForProcess())
+      << "ServiceManager is uninitialized. Did you forget to create a "
+         "content::TestServiceManagerContext?";
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->OverrideBinderForTesting(
+          service_manager::ServiceFilter::ByName(ash::mojom::kServiceName),
+          ash::mojom::SessionController::Name_,
+          base::BindRepeating(&TestSessionController::Bind,
+                              base::Unretained(this)));
+}
+
+TestSessionController::~TestSessionController() {
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ClearBinderOverrideForTesting(
+          service_manager::ServiceFilter::ByName(ash::mojom::kServiceName),
+          ash::mojom::SessionController::Name_);
+}
+
+void TestSessionController::SetClient(
+    ash::mojom::SessionControllerClientPtr client) {}
+
+void TestSessionController::SetSessionInfo(ash::mojom::SessionInfoPtr info) {
+  last_session_info_ = info->Clone();
+}
+
+void TestSessionController::UpdateUserSession(
+    ash::mojom::UserSessionPtr user_session) {
+  last_user_session_ = user_session->Clone();
+  update_user_session_count_++;
+}
+
+void TestSessionController::SetUserSessionOrder(
+    const std::vector<uint32_t>& user_session_order) {}
+
+void TestSessionController::PrepareForLock(PrepareForLockCallback callback) {
+  std::move(callback).Run();
+}
+
+void TestSessionController::StartLock(StartLockCallback callback) {
+  std::move(callback).Run(true);
+}
+
+void TestSessionController::NotifyChromeLockAnimationsComplete() {
+  lock_animation_complete_call_count_++;
+}
+
+void TestSessionController::RunUnlockAnimation(
+    RunUnlockAnimationCallback callback) {
+  std::move(callback).Run();
+}
+
+void TestSessionController::NotifyChromeTerminating() {}
+
+void TestSessionController::SetSessionLengthLimit(base::TimeDelta length_limit,
+                                                  base::TimeTicks start_time) {
+  last_session_length_limit_ = length_limit;
+  last_session_start_time_ = start_time;
+}
+
+void TestSessionController::CanSwitchActiveUser(
+    CanSwitchActiveUserCallback callback) {
+  std::move(callback).Run(true);
+}
+
+void TestSessionController::ShowMultiprofilesIntroDialog(
+    ShowMultiprofilesIntroDialogCallback callback) {
+  std::move(callback).Run(true, false);
+}
+
+void TestSessionController::ShowTeleportWarningDialog(
+    ShowTeleportWarningDialogCallback callback) {
+  std::move(callback).Run(true, false);
+}
+
+void TestSessionController::ShowMultiprofilesSessionAbortedDialog(
+    const std::string& user_email) {}
+
+void TestSessionController::AddSessionActivationObserverForAccountId(
+    const AccountId& account_id,
+    ash::mojom::SessionActivationObserverPtr observer) {}
+
+void TestSessionController::Bind(mojo::ScopedMessagePipeHandle handle) {
+  binding_.Bind(ash::mojom::SessionControllerRequest(std::move(handle)));
+}
diff --git a/chrome/browser/ui/ash/test_session_controller.h b/chrome/browser/ui/ash/test_session_controller.h
new file mode 100644
index 0000000..4318b3e
--- /dev/null
+++ b/chrome/browser/ui/ash/test_session_controller.h
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_TEST_SESSION_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_TEST_SESSION_CONTROLLER_H_
+
+#include <string>
+#include <vector>
+
+#include "ash/public/interfaces/session_controller.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+// Test implementation of ash's mojo SessionController interface.
+//
+// Registers itself to ServiceManager on construction and deregisters
+// on destruction.
+//
+// Note: A ServiceManagerConnection must be initialized before constructing this
+// object. Consider using content::TestServiceManagerContext on your tests.
+class TestSessionController : public ash::mojom::SessionController {
+ public:
+  TestSessionController();
+  ~TestSessionController() override;
+
+  ash::mojom::SessionInfo* last_session_info() {
+    return last_session_info_.get();
+  }
+
+  base::TimeDelta last_session_length_limit() {
+    return last_session_length_limit_;
+  }
+
+  base::TimeTicks last_session_start_time() { return last_session_start_time_; }
+
+  ash::mojom::UserSession* last_user_session() {
+    return last_user_session_.get();
+  }
+
+  int update_user_session_count() { return update_user_session_count_; }
+
+  int lock_animation_complete_call_count() {
+    return lock_animation_complete_call_count_;
+  }
+
+  // ash::mojom::SessionController:
+  void SetClient(ash::mojom::SessionControllerClientPtr client) override;
+  void SetSessionInfo(ash::mojom::SessionInfoPtr info) override;
+  void UpdateUserSession(ash::mojom::UserSessionPtr user_session) override;
+  void SetUserSessionOrder(
+      const std::vector<uint32_t>& user_session_order) override;
+  void PrepareForLock(PrepareForLockCallback callback) override;
+  void StartLock(StartLockCallback callback) override;
+  void NotifyChromeLockAnimationsComplete() override;
+  void RunUnlockAnimation(RunUnlockAnimationCallback callback) override;
+  void NotifyChromeTerminating() override;
+  void SetSessionLengthLimit(base::TimeDelta length_limit,
+                             base::TimeTicks start_time) override;
+  void CanSwitchActiveUser(CanSwitchActiveUserCallback callback) override;
+  void ShowMultiprofilesIntroDialog(
+      ShowMultiprofilesIntroDialogCallback callback) override;
+  void ShowTeleportWarningDialog(
+      ShowTeleportWarningDialogCallback callback) override;
+  void ShowMultiprofilesSessionAbortedDialog(
+      const std::string& user_email) override;
+  void AddSessionActivationObserverForAccountId(
+      const AccountId& account_id,
+      ash::mojom::SessionActivationObserverPtr observer) override;
+
+ private:
+  void Bind(mojo::ScopedMessagePipeHandle handle);
+
+  ash::mojom::SessionInfoPtr last_session_info_;
+  ash::mojom::UserSessionPtr last_user_session_;
+  base::TimeDelta last_session_length_limit_;
+  base::TimeTicks last_session_start_time_;
+  int update_user_session_count_ = 0;
+  int lock_animation_complete_call_count_ = 0;
+  mojo::Binding<ash::mojom::SessionController> binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(TestSessionController);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_TEST_SESSION_CONTROLLER_H_
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index ed5d5fdf..ae8d88d 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -137,6 +137,7 @@
     case ui::NativeTheme::kColorId_HighlightedMenuItemForegroundColor:
     case ui::NativeTheme::kColorId_FocusedHighlightedMenuItemBackgroundColor:
     case ui::NativeTheme::kColorId_MenuItemAlertBackgroundColor:
+    case ui::NativeTheme::kColorId_DefaultIconColor:
       return ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
           color_id);
 
diff --git a/chrome/browser/ui/profile_error_browsertest.cc b/chrome/browser/ui/profile_error_browsertest.cc
index daeb800..4cbb341c 100644
--- a/chrome/browser/ui/profile_error_browsertest.cc
+++ b/chrome/browser/ui/profile_error_browsertest.cc
@@ -111,8 +111,8 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(ProfileErrorBrowserTestInstance,
-                        ProfileErrorBrowserTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(ProfileErrorBrowserTestInstance,
+                         ProfileErrorBrowserTest,
+                         testing::Bool());
 
 }  // namespace
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
index e088ab5..8db025c2 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
@@ -180,10 +180,10 @@
 }
 
 #if defined(OS_WIN)
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     AppMenuIconControllerTest,
     ::testing::Range(0, static_cast<int>(install_static::NUM_INSTALL_MODES)));
 #else
-INSTANTIATE_TEST_CASE_P(, AppMenuIconControllerTest, ::testing::Values(0));
+INSTANTIATE_TEST_SUITE_P(, AppMenuIconControllerTest, ::testing::Values(0));
 #endif
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 85e5d1d..4f852c3 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2859,4 +2859,5 @@
   new_tab_button_bounds_.set_size(new_tab_button_->GetPreferredSize());
   new_tab_button_->SetBoundsRect(new_tab_button_bounds_);
   StopAnimating(true);
+  PreferredSizeChanged();
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
index af5e73cd..c524f7f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
@@ -41,10 +41,6 @@
       "retrainAssistantVoiceModel",
       base::BindRepeating(&GoogleAssistantHandler::HandleRetrainVoiceModel,
                           base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "deleteAssistantVoiceModel",
-      base::BindRepeating(&GoogleAssistantHandler::HandleDeleteVoiceModel,
-                          base::Unretained(this)));
 }
 
 void GoogleAssistantHandler::HandleShowGoogleAssistantSettings(
@@ -67,17 +63,6 @@
       ash::mojom::FlowType::SPEAKER_ID_ENROLLMENT, base::DoNothing());
 }
 
-void GoogleAssistantHandler::HandleDeleteVoiceModel(
-    const base::ListValue* args) {
-  CHECK_EQ(0U, args->GetSize());
-  if (!settings_manager_.is_bound())
-    BindAssistantSettingsManager();
-
-  settings_manager_->RemoveSpeakerIdEnrollmentData(
-      base::BindOnce(&GoogleAssistantHandler::DeleteVoiceModelCallback,
-                     weak_factory_.GetWeakPtr()));
-}
-
 void GoogleAssistantHandler::BindAssistantSettingsManager() {
   DCHECK(!settings_manager_.is_bound());
 
@@ -87,12 +72,5 @@
   connector->BindInterface(assistant::mojom::kServiceName, &settings_manager_);
 }
 
-void GoogleAssistantHandler::DeleteVoiceModelCallback() {
-  // Disable hotword if voice model is deleted.
-  PrefService* prefs = profile_->GetPrefs();
-  prefs->SetBoolean(arc::prefs::kVoiceInteractionHotwordEnabled, false);
-  prefs->SetBoolean(arc::prefs::kVoiceInteractionHotwordAlwaysOn, false);
-}
-
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
index 670583ca..d99ede1 100644
--- a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
@@ -29,8 +29,6 @@
   void HandleShowGoogleAssistantSettings(const base::ListValue* args);
   // WebUI call to retrain Assistant voice model.
   void HandleRetrainVoiceModel(const base::ListValue* args);
-  // WebUI call to delete Assistant voice model.
-  void HandleDeleteVoiceModel(const base::ListValue* args);
 
   // Bind to assistant settings manager.
   void BindAssistantSettingsManager();
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 9bc0e9c..982e64ac 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -54,7 +54,6 @@
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/system/sys_info.h"
@@ -340,9 +339,6 @@
   html_source->AddBoolean(
       "showExperimentalAccessibilitySwitchAccess",
       cmd.HasSwitch(::switches::kEnableExperimentalAccessibilitySwitchAccess));
-
-  html_source->AddBoolean("dockedMagnifierFeatureEnabled",
-                          ash::features::IsDockedMagnifierEnabled());
 #endif
 }
 
@@ -836,9 +832,6 @@
   html_source->AddBoolean("hasExternalTouchDevice",
                           display::HasExternalTouchscreenDevice());
 
-  html_source->AddBoolean("nightLightFeatureEnabled",
-                          ash::features::IsNightLightEnabled());
-
   static constexpr LocalizedString kStorageStrings[] = {
       {"storageTitle", IDS_SETTINGS_STORAGE_TITLE},
       {"storageItemInUse", IDS_SETTINGS_STORAGE_ITEM_IN_USE},
@@ -2238,8 +2231,6 @@
        IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_DESCRIPTION},
       {"googleAssistantVoiceSettingsRetrainButton",
        IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_RETRAIN},
-      {"googleAssistantVoiceSettingsDeleteButton",
-       IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_DELETE},
       {"googleAssistantEnableHotwordWithoutDspDescription",
        IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_WITHOUT_DSP_DESCRIPTION},
       {"googleAssistantEnableHotwordWithoutDspRecommended",
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 4201c661..c18abd8 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -315,14 +315,6 @@
       "clearUsage", base::BindRepeating(&SiteSettingsHandler::HandleClearUsage,
                                         base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "fetchUsbDevices",
-      base::BindRepeating(&SiteSettingsHandler::HandleFetchUsbDevices,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "removeUsbDevice",
-      base::BindRepeating(&SiteSettingsHandler::HandleRemoveUsbDevice,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "setDefaultValueForContentType",
       base::BindRepeating(
           &SiteSettingsHandler::HandleSetDefaultValueForContentType,
@@ -603,49 +595,6 @@
   }
 }
 
-void SiteSettingsHandler::HandleFetchUsbDevices(const base::ListValue* args) {
-  AllowJavascript();
-
-  CHECK_EQ(1U, args->GetSize());
-  const base::Value* callback_id;
-  CHECK(args->Get(0, &callback_id));
-
-  base::ListValue exceptions;
-  const std::string group_name = site_settings::ContentSettingsTypeToGroupName(
-      CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA);
-  const site_settings::ChooserTypeNameEntry* chooser_type =
-      site_settings::ChooserTypeFromGroupName(group_name);
-  // TODO(finnur): Figure out whether incognito permissions are also needed.
-  site_settings::GetChooserExceptionsFromProfile(
-      profile_, false, *chooser_type, &exceptions);
-  ResolveJavascriptCallback(*callback_id, exceptions);
-}
-
-void SiteSettingsHandler::HandleRemoveUsbDevice(const base::ListValue* args) {
-  CHECK_EQ(3U, args->GetSize());
-
-  std::string origin_string;
-  CHECK(args->GetString(0, &origin_string));
-  GURL requesting_origin(origin_string);
-  CHECK(requesting_origin.is_valid());
-
-  std::string embedding_origin_string;
-  CHECK(args->GetString(1, &embedding_origin_string));
-  GURL embedding_origin(embedding_origin_string);
-  CHECK(embedding_origin.is_valid());
-
-  const base::DictionaryValue* object = nullptr;
-  CHECK(args->GetDictionary(2, &object));
-
-  const std::string group_name = site_settings::ContentSettingsTypeToGroupName(
-      CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA);
-  const site_settings::ChooserTypeNameEntry* chooser_type =
-      site_settings::ChooserTypeFromGroupName(group_name);
-  ChooserContextBase* chooser_context = chooser_type->get_context(profile_);
-  chooser_context->RevokeObjectPermission(requesting_origin, embedding_origin,
-                                          *object);
-}
-
 void SiteSettingsHandler::HandleSetDefaultValueForContentType(
     const base::ListValue* args) {
   CHECK_EQ(2U, args->GetSize());
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index cac3cce..d0bc0f6 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -147,12 +147,6 @@
   // Deletes the storage being used for a given host.
   void HandleClearUsage(const base::ListValue* args);
 
-  // Handles the request for a list of all USB devices.
-  void HandleFetchUsbDevices(const base::ListValue* args);
-
-  // Removes a particular USB device permission.
-  void HandleRemoveUsbDevice(const base::ListValue* args);
-
   // Gets and sets the default value for a particular content settings type.
   void HandleSetDefaultValueForContentType(const base::ListValue* args);
   void HandleGetDefaultValueForContentType(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/tts_handler.cc b/chrome/browser/ui/webui/settings/tts_handler.cc
index c3f553c2..31682187 100644
--- a/chrome/browser/ui/webui/settings/tts_handler.cc
+++ b/chrome/browser/ui/webui/settings/tts_handler.cc
@@ -111,6 +111,7 @@
 void TtsHandler::OnTtsEvent(content::TtsUtterance* utterance,
                             content::TtsEventType event_type,
                             int char_index,
+                            int length,
                             const std::string& error_message) {
   if (event_type == content::TTS_EVENT_END ||
       event_type == content::TTS_EVENT_INTERRUPTED ||
diff --git a/chrome/browser/ui/webui/settings/tts_handler.h b/chrome/browser/ui/webui/settings/tts_handler.h
index 93fd5b90..f26fbb2 100644
--- a/chrome/browser/ui/webui/settings/tts_handler.h
+++ b/chrome/browser/ui/webui/settings/tts_handler.h
@@ -37,6 +37,7 @@
   void OnTtsEvent(content::TtsUtterance* utterance,
                   content::TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override;
 
  private:
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index 9b61a6e..f7ae9d36 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -34,7 +34,6 @@
 
 constexpr char kAppName[] = "appName";
 constexpr char kAppId[] = "appId";
-constexpr char kObjectName[] = "objectName";
 
 namespace {
 
@@ -581,135 +580,6 @@
 }
 
 // Create a DictionaryValue* that will act as a data source for a single row
-// in a chooser permission exceptions table.
-std::unique_ptr<base::DictionaryValue> GetChooserExceptionForPage(
-    const GURL& requesting_origin,
-    const GURL& embedding_origin,
-    const std::string& provider_name,
-    bool incognito,
-    const std::string& name,
-    const base::DictionaryValue* object) {
-  std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
-
-  std::string setting_string =
-      content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
-  DCHECK(!setting_string.empty());
-
-  exception->SetString(kSetting, setting_string);
-  exception->SetString(kOrigin, requesting_origin.spec());
-  exception->SetString(kDisplayName, requesting_origin.spec());
-  exception->SetString(kEmbeddingOrigin, embedding_origin.spec());
-  exception->SetString(kSource, provider_name);
-  exception->SetBoolean(kIncognito, incognito);
-  if (object) {
-    exception->SetString(kObjectName, name);
-    exception->Set(kObject, object->CreateDeepCopy());
-  }
-  return exception;
-}
-
-void GetChooserExceptionsFromProfile(Profile* profile,
-                                     bool incognito,
-                                     const ChooserTypeNameEntry& chooser_type,
-                                     base::ListValue* exceptions) {
-  if (incognito) {
-    if (!profile->HasOffTheRecordProfile())
-      return;
-    profile = profile->GetOffTheRecordProfile();
-  }
-
-  ChooserContextBase* chooser_context = chooser_type.get_context(profile);
-  ContentSettingsType content_type =
-      ContentSettingsTypeFromGroupName(std::string(chooser_type.name));
-  std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
-      chooser_context->GetAllGrantedObjects();
-  AllOriginObjects all_origin_objects;
-  for (const auto& object : objects) {
-    // Skip policy controlled objects until they are ready to be displayed.
-    // TODO(https://crbug.com/854329): Include policy controlled objects
-    // when the UI is capable of displaying them properly as policy controlled
-    // objects.
-    if (object->source ==
-        content_settings::SettingSource::SETTING_SOURCE_POLICY) {
-      continue;
-    }
-
-    std::string name = chooser_context->GetObjectName(object->value);
-    std::string source = GetSourceStringForChooserException(
-        profile, content_type, object->source);
-
-    // It is safe for this structure to hold references into |objects| because
-    // they are both destroyed at the end of this function.
-    all_origin_objects[make_pair(object->requesting_origin, source)]
-                      [object->embedding_origin]
-                          .insert(make_pair(name, &object->value));
-  }
-
-  // Keep the exceptions sorted by provider so they will be displayed in
-  // precedence order.
-  std::vector<std::unique_ptr<base::DictionaryValue>>
-      all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
-
-  for (const auto& all_origin_objects_entry : all_origin_objects) {
-    const GURL& requesting_origin = all_origin_objects_entry.first.first;
-    const std::string& source = all_origin_objects_entry.first.second;
-    const OneOriginObjects& one_origin_objects =
-        all_origin_objects_entry.second;
-
-    auto& this_provider_exceptions = all_provider_exceptions
-        [HostContentSettingsMap::GetProviderTypeFromSource(source)];
-
-    // Add entries for any non-embedded origins.
-    bool has_embedded_entries = false;
-    for (const auto& one_origin_objects_entry : one_origin_objects) {
-      const GURL& embedding_origin = one_origin_objects_entry.first;
-      const SortedObjects& sorted_objects = one_origin_objects_entry.second;
-
-      // Skip the embedded settings which will be added below.
-      if (requesting_origin != embedding_origin) {
-        has_embedded_entries = true;
-        continue;
-      }
-
-      for (const auto& sorted_objects_entry : sorted_objects) {
-        this_provider_exceptions.push_back(GetChooserExceptionForPage(
-            requesting_origin, embedding_origin, source, incognito,
-            sorted_objects_entry.first, sorted_objects_entry.second));
-      }
-    }
-
-    if (has_embedded_entries) {
-      // Add a "parent" entry that simply acts as a heading for all entries
-      // where |requesting_origin| has been embedded.
-      this_provider_exceptions.push_back(GetChooserExceptionForPage(
-          requesting_origin, requesting_origin, source, incognito,
-          std::string(), nullptr));
-
-      // Add the "children" for any embedded settings.
-      for (const auto& one_origin_objects_entry : one_origin_objects) {
-        const GURL& embedding_origin = one_origin_objects_entry.first;
-        const SortedObjects& sorted_objects = one_origin_objects_entry.second;
-
-        // Skip the non-embedded setting which we already added above.
-        if (requesting_origin == embedding_origin)
-          continue;
-
-        for (const auto& sorted_objects_entry : sorted_objects) {
-          this_provider_exceptions.push_back(GetChooserExceptionForPage(
-              requesting_origin, embedding_origin, source, incognito,
-              sorted_objects_entry.first, sorted_objects_entry.second));
-        }
-      }
-    }
-  }
-
-  for (auto& one_provider_exceptions : all_provider_exceptions) {
-    for (auto& exception : one_provider_exceptions)
-      exceptions->Append(std::move(exception));
-  }
-}
-
-// Create a DictionaryValue* that will act as a data source for a single row
 // in a chooser permission exceptions table. The chooser permission will contain
 // a list of site exceptions that correspond to the exception.
 std::unique_ptr<base::DictionaryValue> CreateChooserExceptionObject(
diff --git a/chrome/browser/ui/webui/site_settings_helper.h b/chrome/browser/ui/webui/site_settings_helper.h
index bcf7aeaa..1a2e6f9 100644
--- a/chrome/browser/ui/webui/site_settings_helper.h
+++ b/chrome/browser/ui/webui/site_settings_helper.h
@@ -159,16 +159,6 @@
 
 const ChooserTypeNameEntry* ChooserTypeFromGroupName(const std::string& name);
 
-// Fills in |exceptions| with Values for the given |chooser_type| from map.
-void GetChooserExceptionsFromProfile(Profile* profile,
-                                     bool incognito,
-                                     const ChooserTypeNameEntry& chooser_type,
-                                     base::ListValue* exceptions);
-
-// TODO(https://crbug.com/854329): Once the Site Settings WebUI is capable of
-// displaying the new chooser exception object format, replace the existing
-// chooser exception methods with these methods.
-
 // Creates a chooser exception object for the object with |display_name|. The
 // object contains the following properties
 // * displayName: string,
diff --git a/chrome/browser/ui/webui/site_settings_helper_unittest.cc b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
index f8bab84..c0fc59f 100644
--- a/chrome/browser/ui/webui/site_settings_helper_unittest.cc
+++ b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
@@ -505,13 +505,6 @@
 
 }  // namespace
 
-// At the moment, UI strings to describe wildcard device permissions have not
-// been added yet. As a result, the names are inaccurate. Once the strings are
-// added, the device names for policy defined devices will change, and so will
-// the order in which GetChooserExceptionListFromProfile returns these chooser
-// exceptions.
-// TODO(https://crbug.com/854320): Update this unit test when the UI strings are
-// added for the wildcard devices with the new chooser exception order.
 TEST_F(SiteSettingsHelperChooserExceptionTest,
        GetChooserExceptionListFromProfile) {
   const std::string kUsbChooserGroupName =
@@ -534,6 +527,61 @@
   ASSERT_EQ(exceptions->GetSize(), 4u);
   auto& exceptions_list = exceptions->GetList();
 
+  // This exception should describe the permissions for any device with the
+  // vendor ID corresponding to "Google Inc.". There are no user granted
+  // permissions that intersect with this permission, and this policy only
+  // grants one permission to the following site pair:
+  // * ("https://google.com", "https://android.com")
+  {
+    const auto& exception = exceptions_list[0];
+    ExpectDisplayNameEq(exception,
+                        /*display_name=*/"Devices from Google Inc.");
+
+    const auto& sites_list = exception.FindKey(kSites)->GetList();
+    ASSERT_EQ(sites_list.size(), 1u);
+    ExpectValidSiteExceptionObject(sites_list[0],
+                                   /*origin=*/kGoogleOrigin,
+                                   /*embedding_origin=*/kAndroidOrigin,
+                                   /*source=*/kPolicySource,
+                                   /*incognito=*/false);
+  }
+
+  // This exception should describe the permissions for any device.
+  // There are no user granted permissions that intersect with this permission,
+  // and this policy only grants one permission to the following site pair:
+  // * ("https://google.com", "https://google.com")
+  {
+    const auto& exception = exceptions_list[1];
+    ExpectDisplayNameEq(exception,
+                        /*display_name=*/"Devices from any vendor");
+
+    const auto& sites_list = exception.FindKey(kSites)->GetList();
+    ASSERT_EQ(sites_list.size(), 1u);
+    ExpectValidSiteExceptionObject(sites_list[0],
+                                   /*origin=*/kGoogleOrigin,
+                                   /*embedding_origin=*/kGoogleOrigin,
+                                   /*source=*/kPolicySource,
+                                   /*incognito=*/false);
+  }
+
+  // This exception should describe the permissions for any device with the
+  // vendor ID 6354. There is a user granted permission for a device with that
+  // vendor ID, so the site list for this exception will only have the policy
+  // granted permission, which is the following:
+  // * ("https://android.com", "")
+  {
+    const auto& exception = exceptions_list[2];
+    ExpectDisplayNameEq(exception,
+                        /*display_name=*/"Devices from vendor 0x18D2");
+
+    const auto& sites_list = exception.FindKey(kSites)->GetList();
+    ASSERT_EQ(sites_list.size(), 1u);
+    ExpectValidSiteExceptionObject(sites_list[0],
+                                   /*origin=*/kAndroidOrigin,
+                                   /*source=*/kPolicySource,
+                                   /*incognito=*/false);
+  }
+
   // This exception should describe the permissions for the "Gizmo" device.
   // The user granted permissions are the following:
   // * ("https://chromium.org", "https://chromium.org")
@@ -548,7 +596,7 @@
   // * ("https://chromium.org", "")
   // * ("https://android.com", "https://chromium.org")
   {
-    const auto& exception = exceptions_list[0];
+    const auto& exception = exceptions_list[3];
     ExpectDisplayNameEq(exception, /*display_name=*/"Gizmo");
 
     const auto& sites_list = exception.FindKey(kSites)->GetList();
@@ -563,58 +611,6 @@
                                    /*source=*/kPreferenceSource,
                                    /*incognito=*/false);
   }
-
-  // This exception should describe the permissions for any device with the
-  // vendor ID 6354. There is a user granted permission for a device with that
-  // vendor ID, so the site list for this exception will only have the policy
-  // granted permission, which is the following:
-  // * ("https://android.com", "")
-  {
-    const auto& exception = exceptions_list[1];
-    ExpectDisplayNameEq(exception,
-                        /*display_name=*/"Unknown device [18d2:ffffffff]");
-
-    const auto& sites_list = exception.FindKey(kSites)->GetList();
-    ExpectValidSiteExceptionObject(sites_list[0],
-                                   /*origin=*/kAndroidOrigin,
-                                   /*source=*/kPolicySource,
-                                   /*incognito=*/false);
-  }
-
-  // This exception should describe the permissions for any device.
-  // There are no user granted permissions that intersect with this permission,
-  // and this policy only grants one permission to the following site pair:
-  // * ("https://google.com", "https://google.com")
-  {
-    const auto& exception = exceptions_list[2];
-    ExpectDisplayNameEq(exception,
-                        /*display_name=*/"Unknown device [ffffffff:ffffffff]");
-
-    const auto& sites_list = exception.FindKey(kSites)->GetList();
-    ExpectValidSiteExceptionObject(sites_list[0],
-                                   /*origin=*/kGoogleOrigin,
-                                   /*embedding_origin=*/kGoogleOrigin,
-                                   /*source=*/kPolicySource,
-                                   /*incognito=*/false);
-  }
-
-  // This exception should describe the permissions for any device with the
-  // vendor ID corresponding to "Google Inc.". There are no user granted
-  // permissions that intersect with this permission, and this policy only
-  // grants one permission to the following site pair:
-  // * ("https://google.com", "https://android.com")
-  {
-    const auto& exception = exceptions_list[3];
-    ExpectDisplayNameEq(exception,
-                        /*display_name=*/"Unknown device from Google Inc.");
-
-    const auto& sites_list = exception.FindKey(kSites)->GetList();
-    ExpectValidSiteExceptionObject(sites_list[0],
-                                   /*origin=*/kGoogleOrigin,
-                                   /*embedding_origin=*/kAndroidOrigin,
-                                   /*source=*/kPolicySource,
-                                   /*incognito=*/false);
-  }
 }
 
 }  // namespace site_settings
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index 6b6a5e4..a4429ea9 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -32,6 +32,7 @@
 constexpr char kProductIdKey[] = "product-id";
 constexpr char kSerialNumberKey[] = "serial-number";
 constexpr char kVendorIdKey[] = "vendor-id";
+constexpr int kDeviceIdWildcard = -1;
 
 // Reasons a permission may be closed. These are used in histograms so do not
 // remove/reorder entries. Only add at the end just before
@@ -66,12 +67,6 @@
 }
 
 base::string16 GetDeviceNameFromIds(int vendor_id, int product_id) {
-// This is currently using the UI strings used for the chooser prompt. This is
-// fine for now since the policy allowed devices are not being displayed in
-// Site Settings yet. However, policy allowed devices can contain wildcards for
-// the IDs, so more specific UI string need to be defined.
-// TODO(https://crbug.com/854329): Add UI strings that are more specific to
-// the Site Settings UI.
 #if !defined(OS_ANDROID)
   const char* product_name =
       device::UsbIds::GetProductName(vendor_id, product_id);
@@ -80,15 +75,31 @@
 
   const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
   if (vendor_name) {
+    if (product_id == kDeviceIdWildcard) {
+      return l10n_util::GetStringFUTF16(IDS_DEVICE_DESCRIPTION_FOR_VENDOR_NAME,
+                                        base::UTF8ToUTF16(vendor_name));
+    }
+
     return l10n_util::GetStringFUTF16(
-        IDS_DEVICE_CHOOSER_DEVICE_NAME_UNKNOWN_DEVICE_WITH_VENDOR_NAME,
+        IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_NAME,
+        base::ASCIIToUTF16(base::StringPrintf("0x%04X", product_id)),
         base::UTF8ToUTF16(vendor_name));
   }
 #endif  // !defined(OS_ANDROID)
+
+  if (product_id == kDeviceIdWildcard) {
+    if (vendor_id == kDeviceIdWildcard)
+      return l10n_util::GetStringUTF16(IDS_DEVICE_DESCRIPTION_FOR_ANY_VENDOR);
+
+    return l10n_util::GetStringFUTF16(
+        IDS_DEVICE_DESCRIPTION_FOR_VENDOR_ID,
+        base::ASCIIToUTF16(base::StringPrintf("0x%04X", vendor_id)));
+  }
+
   return l10n_util::GetStringFUTF16(
-      IDS_DEVICE_CHOOSER_DEVICE_NAME_UNKNOWN_DEVICE_WITH_VENDOR_ID_AND_PRODUCT_ID,
-      base::ASCIIToUTF16(base::StringPrintf("%04x", vendor_id)),
-      base::ASCIIToUTF16(base::StringPrintf("%04x", product_id)));
+      IDS_DEVICE_DESCRIPTION_FOR_PRODUCT_ID_AND_VENDOR_ID,
+      base::ASCIIToUTF16(base::StringPrintf("0x%04X", product_id)),
+      base::ASCIIToUTF16(base::StringPrintf("0x%04X", vendor_id)));
 }
 
 std::unique_ptr<base::DictionaryValue> DeviceIdsToDictValue(int vendor_id,
diff --git a/chrome/browser/usb/usb_chooser_context_unittest.cc b/chrome/browser/usb/usb_chooser_context_unittest.cc
index 3a198941..5139c651 100644
--- a/chrome/browser/usb/usb_chooser_context_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_context_unittest.cc
@@ -640,21 +640,21 @@
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+                          /*name=*/"Devices from any vendor");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Devices from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Unknown product 0x162E from Google Inc.");
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
@@ -662,7 +662,7 @@
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
-                          /*name=*/"Unknown device [18d2:054d]");
+                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
 }
 
 TEST_F(UsbChooserContextTest,
@@ -720,21 +720,21 @@
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+                          /*name=*/"Devices from any vendor");
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Devices from Google Inc.");
   ExpectChooserObjectInfo(objects[4].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Unknown product 0x162E from Google Inc.");
   ExpectChooserObjectInfo(objects[5].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
@@ -742,7 +742,7 @@
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
-                          /*name=*/"Unknown device [18d2:054d]");
+                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
 }
 
 TEST_F(UsbChooserContextTest,
@@ -775,14 +775,14 @@
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+                          /*name=*/"Devices from any vendor");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Devices from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
@@ -797,7 +797,7 @@
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
-                          /*name=*/"Unknown device [18d2:054d]");
+                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
   ASSERT_TRUE(persistent_device_info->product_name);
   EXPECT_EQ(base::UTF8ToUTF16(store->GetObjectName(objects[2]->value)),
             persistent_device_info->product_name.value());
@@ -833,21 +833,21 @@
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+                          /*name=*/"Devices from any vendor");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Devices from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Unknown product 0x162E from Google Inc.");
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
@@ -855,7 +855,7 @@
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
-                          /*name=*/"Unknown device [18d2:054d]");
+                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
 }
 
 TEST_F(UsbChooserContextTest,
@@ -888,21 +888,21 @@
                           /*incognito=*/false,
                           /*vendor_id=*/kDeviceIdWildcard,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device [ffffffff:ffffffff]");
+                          /*name=*/"Devices from any vendor");
   ExpectChooserObjectInfo(objects[1].get(),
                           /*requesting_origin=*/kVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/kDeviceIdWildcard,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Devices from Google Inc.");
   ExpectChooserObjectInfo(objects[2].get(),
                           /*requesting_origin=*/kProductVendorOrigin,
                           /*source=*/content_settings::SETTING_SOURCE_POLICY,
                           /*incognito=*/false,
                           /*vendor_id=*/6353,
                           /*product_id=*/5678,
-                          /*name=*/"Unknown device from Google Inc.");
+                          /*name=*/"Unknown product 0x162E from Google Inc.");
   ExpectChooserObjectInfo(objects[3].get(),
                           /*requesting_origin=*/kGadgetOrigin,
                           /*embedding_origin=*/kCoolOrigin,
@@ -910,5 +910,5 @@
                           /*incognito=*/false,
                           /*vendor_id=*/6354,
                           /*product_id=*/1357,
-                          /*name=*/"Unknown device [18d2:054d]");
+                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
 }
diff --git a/chrome/common/extensions/api/tts.json b/chrome/common/extensions/api/tts.json
index 8928d14..5a83652 100644
--- a/chrome/common/extensions/api/tts.json
+++ b/chrome/common/extensions/api/tts.json
@@ -25,16 +25,16 @@
         "properties": {
           "type": {
             "$ref": "EventType",
-            "description": "The type can be 'start' as soon as speech has started, 'word' when a word boundary is reached, 'sentence' when a sentence boundary is reached, 'marker' when an SSML mark element is reached, 'end' when the end of the utterance is reached, 'interrupted' when the utterance is stopped or interrupted before reaching the end, 'cancelled' when it's removed from the queue before ever being synthesized, or 'error' when any other error occurs. When pausing speech, a 'pause' event is fired if a particular utterance is paused in the middle, and 'resume' if an utterance resumes speech. Note that pause and resume events may not fire if speech is paused in-between utterances."
+            "description": "The type can be <code>start</code> as soon as speech has started, <code>word</code> when a word boundary is reached, <code>sentence</code> when a sentence boundary is reached, <code>marker</code> when an SSML mark element is reached, <code>end</code> when the end of the utterance is reached, <code>interrupted</code> when the utterance is stopped or interrupted before reaching the end, <code>cancelled</code> when it's removed from the queue before ever being synthesized, or <code>error</code> when any other error occurs. When pausing speech, a <code>pause</code> event is fired if a particular utterance is paused in the middle, and <code>resume</code> if an utterance resumes speech. Note that pause and resume events may not fire if speech is paused in-between utterances."
           },
           "charIndex": {
             "type": "integer",
             "optional": true,
-            "description": "The index of the current character in the utterance."
+            "description": "The index of the current character in the utterance. For word events, the event fires at the end of one word and before the beginning of the next. The <code>charIndex</code> represents a point in the text at the beginning of the next word to be spoken."
           },
           "errorMessage": {
             "type": "string",
-            "description": "The error description, if the event type is 'error'.",
+            "description": "The error description, if the event type is <code>error</code>.",
             "optional": true
           },
           "srcId": {
@@ -48,6 +48,11 @@
             "description": "True if this is the final event that will be sent to this handler.",
             "nodoc": true,
             "optional": true
+          },
+          "length": {
+            "type": "integer",
+            "optional": true,
+            "description": "The length of the next part of the utterance. For example, in a <code>word</code> event, this is the length of the word which will be spoken next. It will be set to -1 if not set by the speech engine."
           }
         }
       },
diff --git a/chrome/common/trace_event_args_whitelist.cc b/chrome/common/trace_event_args_whitelist.cc
index f233f9e..034beee 100644
--- a/chrome/common/trace_event_args_whitelist.cc
+++ b/chrome/common/trace_event_args_whitelist.cc
@@ -22,6 +22,9 @@
 const char* const kGPUAllowedArgs[] = {nullptr};
 const char* const kInputLatencyAllowedArgs[] = {"data", nullptr};
 const char* const kMemoryDumpAllowedArgs[] = {"dumps", nullptr};
+const char* const kRendererHostAllowedArgs[] = {
+    "class",           "line", "should_background", "has_pending_views",
+    "bytes_allocated", nullptr};
 const char* const kV8GCAllowedArgs[] = {"num_items", "num_tasks", nullptr};
 
 const WhitelistEntry kEventArgsWhitelist[] = {
@@ -38,6 +41,7 @@
     {"ipc", "GpuChannelHost::Send", nullptr},
     {"ipc", "SyncChannel::Send", nullptr},
     {"latencyInfo", "*", kInputLatencyAllowedArgs},
+    {"renderer_host", "*", kRendererHostAllowedArgs},
     {"shutdown", "*", nullptr},
     {"startup", "PrefProvider::PrefProvider", nullptr},
     {"task_scheduler", "*", nullptr},
diff --git a/chrome/renderer/resources/extensions/tts_custom_bindings.js b/chrome/renderer/resources/extensions/tts_custom_bindings.js
index 9d045f7..2bc4166a 100644
--- a/chrome/renderer/resources/extensions/tts_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tts_custom_bindings.js
@@ -23,6 +23,7 @@
       eventHandler({
                      type: event.type,
                      charIndex: event.charIndex,
+                     length: event.length,
                      errorMessage: event.errorMessage
                    });
       if (event.isFinalEvent) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f5536df..f5d7d19 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2626,6 +2626,8 @@
     "../browser/password_manager/password_store_mac_unittest.cc",
     "../browser/password_manager/password_store_x_unittest.cc",
     "../browser/payments/payment_handler_permission_context_unittest.cc",
+    "../browser/performance_monitor/system_monitor_helper_win_unittest.cc",
+    "../browser/performance_monitor/system_monitor_unittest.cc",
     "../browser/permissions/chooser_context_base_mock_permission_observer.cc",
     "../browser/permissions/chooser_context_base_mock_permission_observer.h",
     "../browser/permissions/chooser_context_base_unittest.cc",
@@ -3289,8 +3291,8 @@
       "../browser/ui/toolbar/toolbar_actions_model_unittest.cc",
       "../browser/ui/webui/browsing_history_handler_unittest.cc",
       "../browser/ui/webui/dark_mode_handler_unittest.cc",
-      "../browser/ui/webui/downloads/downloads_list_tracker_unittest.cc",
       "../browser/ui/webui/downloads/downloads_dom_handler_unittest.cc",
+      "../browser/ui/webui/downloads/downloads_list_tracker_unittest.cc",
       "../browser/ui/webui/downloads/mock_downloads_page.cc",
       "../browser/ui/webui/downloads/mock_downloads_page.h",
       "../browser/ui/webui/help/version_updater_chromeos_unittest.cc",
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 47a93936..7973128 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1410,31 +1410,6 @@
  * @constructor
  * @extends {CrSettingsBrowserTest}
  */
-function CrSettingsUsbDevicesTest() {}
-
-CrSettingsUsbDevicesTest.prototype = {
-  __proto__: CrSettingsBrowserTest.prototype,
-
-  /** @override */
-  browsePreload: 'chrome://settings/privacy_page/privacy_page.html',
-
-  /** @override */
-  extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
-    '../test_browser_proxy.js',
-    'test_util.js',
-    'test_site_settings_prefs_browser_proxy.js',
-    'usb_devices_tests.js',
-  ]),
-};
-
-TEST_F('CrSettingsUsbDevicesTest', 'All', function() {
-  mocha.run();
-});
-
-/**
- * @constructor
- * @extends {CrSettingsBrowserTest}
- */
 function CrSettingsProtocolHandlersTest() {}
 
 CrSettingsProtocolHandlersTest.prototype = {
diff --git a/chrome/test/data/webui/settings/site_details_tests.js b/chrome/test/data/webui/settings/site_details_tests.js
index 2e3dd78..a06980a8 100644
--- a/chrome/test/data/webui/settings/site_details_tests.js
+++ b/chrome/test/data/webui/settings/site_details_tests.js
@@ -86,9 +86,12 @@
       test_util.createContentSettingTypeToValuePair(
           settings.ContentSettingsTypes.PAYMENT_HANDLER,
           [test_util.createRawSiteException('https://foo.com:443')]),
+    ], [
       test_util.createContentSettingTypeToValuePair(
           settings.ContentSettingsTypes.USB_DEVICES,
-          [test_util.createRawSiteException('https://foo.com:443')]),
+          [test_util.createRawChooserException(
+              settings.ChooserType.USB_DEVICES,
+              [test_util.createRawSiteException('https://foo.com:443')])]),
     ]);
 
     browserProxy = new TestSiteSettingsPrefsBrowserProxy();
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
index a81b29e..fe6deacf 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
@@ -26,7 +26,6 @@
     super([
       'clearFlashPref',
       'fetchBlockAutoplayStatus',
-      'fetchUsbDevices',
       'fetchZoomLevels',
       'getAllSites',
       'getChooserExceptionList',
@@ -40,7 +39,6 @@
       'observeProtocolHandlersEnabledState',
       'removeIgnoredHandler',
       'removeProtocolHandler',
-      'removeUsbDevice',
       'removeZoomLevel',
       'resetCategoryPermissionForPattern',
       'resetChooserExceptionForSite',
@@ -61,9 +59,6 @@
     /** @private {!Array<ZoomLevelEntry>} */
     this.zoomList_ = [];
 
-    /** @private {!Array<!UsbDeviceEntry>} */
-    this.usbDevices_ = [];
-
     /** @private {!Array<!ProtocolEntry>} */
     this.protocolHandlers_ = [];
 
@@ -146,15 +141,6 @@
 
   /**
    * Sets the prefs to use when testing.
-   * @param {!Array<UsbDeviceEntry>} list The usb device entry list to set.
-   */
-  setUsbDevices(list) {
-    // Shallow copy of the passed-in array so mutation won't impact the source
-    this.usbDevices_ = list.slice();
-  }
-
-  /**
-   * Sets the prefs to use when testing.
    * @param {!Array<ProtocolEntry>} list The protocol handlers list to set.
    */
   setProtocolHandlers(list) {
@@ -360,13 +346,28 @@
     contentTypes.forEach(function(contentType) {
       let setting;
       let source;
-      this.prefs_.exceptions[contentType].some((originPrefs) => {
+      let isSet = this.prefs_.exceptions[contentType].some(originPrefs => {
         if (originPrefs.origin == origin) {
           setting = originPrefs.setting;
           source = originPrefs.source;
           return true;
         }
+        return false;
       });
+
+      if (!isSet) {
+        this.prefs_.chooserExceptions[contentType].some(chooserException => {
+          return chooserException.sites.some(originPrefs => {
+            if (originPrefs.origin == origin) {
+              setting = originPrefs.setting;
+              source = originPrefs.source;
+              return true;
+            }
+            return false;
+          });
+        });
+      }
+
       assert(
           setting != undefined,
           'There was no exception set for origin: ' + origin +
@@ -405,17 +406,6 @@
   }
 
   /** @override */
-  fetchUsbDevices() {
-    this.methodCalled('fetchUsbDevices');
-    return Promise.resolve(this.usbDevices_);
-  }
-
-  /** @override */
-  removeUsbDevice() {
-    this.methodCalled('removeUsbDevice', arguments);
-  }
-
-  /** @override */
   observeProtocolHandlers() {
     cr.webUIListenerCallback('setHandlersEnabled', true);
     cr.webUIListenerCallback('setProtocolHandlers', this.protocolHandlers_);
diff --git a/chrome/test/data/webui/settings/usb_devices_tests.js b/chrome/test/data/webui/settings/usb_devices_tests.js
deleted file mode 100644
index 13f92c1..0000000
--- a/chrome/test/data/webui/settings/usb_devices_tests.js
+++ /dev/null
@@ -1,145 +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.
-
-/** @fileoverview Suite of tests for usb_devices. */
-suite('UsbDevices', function() {
-  /**
-   * A dummy usb-devices element created before each test.
-   * @type {UsbDevices}
-   */
-  let testElement;
-
-  /**
-   * The mock proxy object to use during test.
-   * @type {TestSiteSettingsPrefsBrowserProxy}
-   */
-  let browserProxy = null;
-
-  /**
-   * An example USB device entry list.
-   * @type {!Array<UsbDeviceEntry>}
-   */
-  const deviceList = [
-    {
-      embeddingOrigin: 'device-1-embedding-origin',
-      object: {
-        name: 'device-1',
-        'product-id': 1,
-        'serial-number': 'device-1-sn',
-        'vendor-id': 1
-      },
-      objectName: 'device-1',
-      origin: 'device-1-origin',
-      setting: 'device-1-settings',
-      source: 'device-1-source'
-    },
-    {
-      embeddingOrigin: 'device-2-embedding-origin',
-      object: {
-        name: 'device-2',
-        'product-id': 2,
-        'serial-number': 'device-2-sn',
-        'vendor-id': 2
-      },
-      objectName: 'device-2',
-      origin: 'device-2-origin',
-      setting: 'device-2-settings',
-      source: 'device-2-source'
-    }
-  ];
-
-  setup(function() {
-    browserProxy = new TestSiteSettingsPrefsBrowserProxy();
-    settings.SiteSettingsPrefsBrowserProxyImpl.instance_ = browserProxy;
-  });
-
-  teardown(function() {
-    testElement.remove();
-    testElement = null;
-  });
-
-  /** @return {!Promise} */
-  function initPage() {
-    browserProxy.reset();
-    PolymerTest.clearBody();
-    testElement = document.createElement('usb-devices');
-    document.body.appendChild(testElement);
-    return browserProxy.whenCalled('fetchUsbDevices').then(function() {
-      Polymer.dom.flush();
-    });
-  }
-
-  test('empty devices list', function() {
-    return initPage().then(function() {
-      const listItems = testElement.root.querySelectorAll('.list-item');
-      assertEquals(0, listItems.length);
-    });
-  });
-
-  test('non-empty device list', function() {
-    browserProxy.setUsbDevices(deviceList);
-
-    return initPage().then(function() {
-      const listItems = testElement.root.querySelectorAll('.list-item');
-      assertEquals(deviceList.length, listItems.length);
-    });
-  });
-
-  test('non-empty device list has working menu buttons', function() {
-    browserProxy.setUsbDevices(deviceList);
-
-    return initPage().then(function() {
-      const menuButton =
-          testElement.$$('paper-icon-button-light.icon-more-vert');
-      assertTrue(!!menuButton);
-      menuButton.querySelector('button').click();
-      const dialog = testElement.$$('cr-action-menu');
-      assertTrue(dialog.open);
-    });
-  });
-
-  /**
-   * A reusable function to test removing different devices.
-   * @param {!number} indexToRemove index of devices to be removed.
-   * @return {!Promise}
-   */
-  function testRemovalFlow(indexToRemove) {
-    /**
-     * Test whether or not clicking remove-button sends the correct
-     * parameters to the browserProxy.removeUsbDevice() function.
-     */
-    const menuButton = testElement.root.querySelectorAll(
-        'paper-icon-button-light.icon-more-vert')[indexToRemove];
-    const removeButton = testElement.$.removeButton;
-    menuButton.querySelector('button').click();
-    removeButton.click();
-    return browserProxy.whenCalled('removeUsbDevice').then(function(args) {
-      /**
-       * removeUsbDevice() is expected to be called with arguments as
-       * [origin, embeddingOrigin, object].
-       */
-      assertEquals(deviceList[indexToRemove].origin, args[0]);
-      assertEquals(deviceList[indexToRemove].embeddingOrigin, args[1]);
-      assertEquals(deviceList[indexToRemove].object, args[2]);
-
-      const dialog = testElement.$$('cr-action-menu');
-      assertFalse(dialog.open);
-    });
-  }
-
-  test('try removing items using remove button', function() {
-    browserProxy.setUsbDevices(deviceList);
-
-    const self = this;
-
-    return initPage()
-        .then(function() {
-          return testRemovalFlow(0);
-        })
-        .then(function() {
-          browserProxy.reset();
-          return testRemovalFlow(1);
-        });
-  });
-});
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 328b9f0..a492b896 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -326,6 +326,13 @@
 
 bool CastContentBrowserClient::OverridesAudioManager() {
   // See CreateAudioManager().
+#if defined(OS_ANDROID)
+  // Disable CMA backend on builds older than N.
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_NOUGAT) {
+    return false;
+  }
+#endif  // defined(OS_ANDROID)
   return true;
 }
 #endif  // BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
diff --git a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc
index 18e0ac9..3f04457 100644
--- a/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc
+++ b/chromecast/device/bluetooth/le/gatt_client_manager_impl_test.cc
@@ -62,6 +62,7 @@
   service.handle = 0x1;
   service.primary = true;
 
+  // Generate a characteristic that supports notification only.
   characteristic.uuid = {{0x1, 0x1}};
   characteristic.handle = 0x2;
   characteristic.permissions =
@@ -85,6 +86,7 @@
   characteristic.descriptors.push_back(descriptor);
   service.characteristics.push_back(characteristic);
 
+  // Generate a characteristic that does not support notification or indication.
   characteristic.uuid = {{0x1, 0x2}};
   characteristic.handle = 0x5;
   characteristic.permissions =
@@ -96,10 +98,61 @@
   characteristic.descriptors.clear();
   service.characteristics.push_back(characteristic);
 
+  // Generate a characteristic that supports indication only.
+  characteristic.uuid = {{0x1, 0x3}};
+  characteristic.handle = 0x6;
+  characteristic.permissions =
+      static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+          bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+          bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_INDICATE;
+
+  descriptor.uuid = {{0x1, 0x3, 0x1}};
+  descriptor.handle = 0x7;
+  descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+      bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+      bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.descriptors.push_back(descriptor);
+
+  descriptor.uuid = RemoteDescriptor::kCccdUuid;
+  descriptor.handle = 0x8;
+  descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+      bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+      bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.descriptors.push_back(descriptor);
+  service.characteristics.push_back(characteristic);
+
+  // Generate a characteristic that supports both notification and indication.
+  characteristic.uuid = {{0x1, 0x4}};
+  characteristic.handle = 0x9;
+  characteristic.permissions =
+      static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+          bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+          bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.properties = static_cast<bluetooth_v2_shlib::Gatt::Properties>(
+      bluetooth_v2_shlib::Gatt::PROPERTY_NOTIFY |
+      bluetooth_v2_shlib::Gatt::PROPERTY_INDICATE);
+  characteristic.descriptors.clear();
+
+  descriptor.uuid = {{0x1, 0x4, 0x1}};
+  descriptor.handle = 0xA;
+  descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+      bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+      bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.descriptors.push_back(descriptor);
+
+  descriptor.uuid = RemoteDescriptor::kCccdUuid;
+  descriptor.handle = 0xB;
+  descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
+      bluetooth_v2_shlib::Gatt::PERMISSION_READ |
+      bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
+  characteristic.descriptors.push_back(descriptor);
+  service.characteristics.push_back(characteristic);
+
   ret.push_back(service);
 
   service.uuid = {{0x2}};
-  service.handle = 0x6;
+  service.handle = 0xC;
   service.primary = true;
   service.characteristics.clear();
   ret.push_back(service);
@@ -707,6 +760,80 @@
   fake_task_runner_->RunUntilIdle();
 }
 
+TEST_F(GattClientManagerTest, RemoteDeviceCharacteristicSetRegisterIndication) {
+  const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
+  const auto kServices = GenerateServices();
+  Connect(kTestAddr1);
+  scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
+  bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
+      gatt_client_->delegate();
+  delegate->OnServicesAdded(kTestAddr1, kServices);
+  std::vector<scoped_refptr<RemoteService>> services =
+      GetServices(device.get());
+  ASSERT_EQ(kServices.size(), services.size());
+
+  scoped_refptr<RemoteService> service = services[0];
+  std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
+      service->GetCharacteristics();
+  ASSERT_EQ(characteristics.size(), 4ul);
+
+  // |characteristics[2]| supports indication only.
+  scoped_refptr<RemoteCharacteristic> characteristic = characteristics[2];
+
+  scoped_refptr<RemoteDescriptor> cccd =
+      characteristic->GetDescriptorByUuid(RemoteDescriptor::kCccdUuid);
+  ASSERT_TRUE(cccd);
+
+  EXPECT_CALL(*gatt_client_,
+              SetCharacteristicNotification(
+                  kTestAddr1, characteristic->characteristic(), true))
+      .WillOnce(Return(true));
+  std::vector<uint8_t> cccd_enable_indication = {
+      std::begin(bluetooth::RemoteDescriptor::kEnableIndicationValue),
+      std::end(bluetooth::RemoteDescriptor::kEnableIndicationValue)};
+  EXPECT_CALL(*gatt_client_, WriteDescriptor(kTestAddr1, cccd->descriptor(), _,
+                                             cccd_enable_indication))
+      .WillOnce(Return(true));
+
+  characteristic->SetRegisterNotificationOrIndication(true, cb_.Get());
+  EXPECT_CALL(cb_, Run(true));
+  delegate->OnDescriptorWriteResponse(kTestAddr1, true, cccd->handle());
+
+  EXPECT_CALL(*observer_,
+              OnCharacteristicNotification(device, characteristic, kTestData1));
+  delegate->OnNotification(kTestAddr1, characteristic->handle(), kTestData1);
+  fake_task_runner_->RunUntilIdle();
+
+  // |characteristics[3]| supports both notification and indication.
+  characteristic = characteristics[3];
+
+  cccd = characteristic->GetDescriptorByUuid(RemoteDescriptor::kCccdUuid);
+  ASSERT_TRUE(cccd);
+
+  // Notification has higher priority than indication. So
+  // SetRegisterNotificationOrIndication will behave the same as
+  // SetRegisterNotification.
+  EXPECT_CALL(*gatt_client_,
+              SetCharacteristicNotification(
+                  kTestAddr1, characteristic->characteristic(), true))
+      .WillOnce(Return(true));
+  std::vector<uint8_t> cccd_enable_notification = {
+      std::begin(bluetooth::RemoteDescriptor::kEnableNotificationValue),
+      std::end(bluetooth::RemoteDescriptor::kEnableNotificationValue)};
+  EXPECT_CALL(*gatt_client_, WriteDescriptor(kTestAddr1, cccd->descriptor(), _,
+                                             cccd_enable_notification))
+      .WillOnce(Return(true));
+
+  characteristic->SetRegisterNotificationOrIndication(true, cb_.Get());
+  EXPECT_CALL(cb_, Run(true));
+  delegate->OnDescriptorWriteResponse(kTestAddr1, true, cccd->handle());
+
+  EXPECT_CALL(*observer_,
+              OnCharacteristicNotification(device, characteristic, kTestData1));
+  delegate->OnNotification(kTestAddr1, characteristic->handle(), kTestData1);
+  fake_task_runner_->RunUntilIdle();
+}
+
 TEST_F(GattClientManagerTest, RemoteDeviceDescriptor) {
   const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
   const std::vector<uint8_t> kTestData2 = {0x4, 0x5, 0x6};
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 3285559..8c5de56 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -36,7 +36,7 @@
 // Enables or disables Discover Application on Chrome OS.
 // If enabled, Discover App will be shown in launcher.
 const base::Feature kDiscoverApp{"DiscoverApp",
-                                 base::FEATURE_ENABLED_BY_DEFAULT};
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
 
 // If enabled, DriveFS will be used for Drive sync.
 const base::Feature kDriveFs{"DriveFS", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.cc b/chromeos/services/assistant/assistant_settings_manager_impl.cc
index 4f596dd..087087ec 100644
--- a/chromeos/services/assistant/assistant_settings_manager_impl.cc
+++ b/chromeos/services/assistant/assistant_settings_manager_impl.cc
@@ -164,41 +164,6 @@
       });
 }
 
-void AssistantSettingsManagerImpl::RemoveSpeakerIdEnrollmentData(
-    RemoveSpeakerIdEnrollmentDataCallback callback) {
-  DCHECK(assistant_manager_service_->GetState() ==
-         AssistantManagerService::State::RUNNING);
-  DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence());
-
-  if (!assistant_manager_service_->assistant_manager_internal()) {
-    std::move(callback).Run();
-    return;
-  }
-
-  const std::string device_id =
-      assistant_manager_service_->assistant_manager()->GetDeviceId();
-  if (device_id.empty())
-    return;
-
-  // Update device id and device type.
-  assistant::SettingsUiUpdate update;
-  assistant::AssistantDeviceSettingsUpdate* device_settings_update =
-      update.mutable_assistant_device_settings_update()
-          ->add_assistant_device_settings_update();
-  device_settings_update->set_device_id(device_id);
-  device_settings_update->set_assistant_device_type(
-      assistant::AssistantDevice::CROS);
-
-  device_settings_update->mutable_device_settings()->set_speaker_id_enabled(
-      false);
-
-  UpdateSettings(
-      update.SerializeAsString(),
-      base::BindOnce(
-          &AssistantSettingsManagerImpl::HandleRemoveSpeakerIdEnrollmentData,
-          weak_factory_.GetWeakPtr(), std::move(callback)));
-}
-
 void AssistantSettingsManagerImpl::HandleSpeakerIdEnrollmentUpdate(
     const assistant_client::SpeakerIdEnrollmentUpdate& update) {
   DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence());
@@ -258,14 +223,6 @@
   }
 }
 
-void AssistantSettingsManagerImpl::HandleRemoveSpeakerIdEnrollmentData(
-    RemoveSpeakerIdEnrollmentDataCallback callback,
-    const std::string& result) {
-  // Device settings update result is not handled because it is not included in
-  // the SettingsUiUpdateResult.
-  std::move(callback).Run();
-}
-
 void AssistantSettingsManagerImpl::HandleStopSpeakerIdEnrollment(
     base::RepeatingCallback<void()> callback) {
   DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence());
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.h b/chromeos/services/assistant/assistant_settings_manager_impl.h
index 39a5946..9876540 100644
--- a/chromeos/services/assistant/assistant_settings_manager_impl.h
+++ b/chromeos/services/assistant/assistant_settings_manager_impl.h
@@ -42,8 +42,6 @@
       mojom::SpeakerIdEnrollmentClientPtr client) override;
   void StopSpeakerIdEnrollment(
       StopSpeakerIdEnrollmentCallback callback) override;
-  void RemoveSpeakerIdEnrollmentData(
-      RemoveSpeakerIdEnrollmentDataCallback callback) override;
 
   // AssistantSettingsManager overrides:
   void BindRequest(mojom::AssistantSettingsManagerRequest request) override;
@@ -57,9 +55,6 @@
   void HandleStopSpeakerIdEnrollment(base::RepeatingCallback<void()> callback);
   void HandleSpeakerIdEnrollmentStatusSync(
       const assistant_client::SpeakerIdEnrollmentUpdate& update);
-  void HandleRemoveSpeakerIdEnrollmentData(
-      RemoveSpeakerIdEnrollmentDataCallback callback,
-      const std::string& result);
 
   Service* const service_;
   AssistantManagerServiceImpl* const assistant_manager_service_;
diff --git a/chromeos/services/assistant/fake_assistant_settings_manager_impl.cc b/chromeos/services/assistant/fake_assistant_settings_manager_impl.cc
index 356185f1..bf766af 100644
--- a/chromeos/services/assistant/fake_assistant_settings_manager_impl.cc
+++ b/chromeos/services/assistant/fake_assistant_settings_manager_impl.cc
@@ -36,11 +36,6 @@
   std::move(callback).Run();
 }
 
-void FakeAssistantSettingsManagerImpl::RemoveSpeakerIdEnrollmentData(
-    RemoveSpeakerIdEnrollmentDataCallback callback) {
-  std::move(callback).Run();
-}
-
 void FakeAssistantSettingsManagerImpl::BindRequest(
     mojom::AssistantSettingsManagerRequest request) {
   bindings_.AddBinding(this, std::move(request));
diff --git a/chromeos/services/assistant/fake_assistant_settings_manager_impl.h b/chromeos/services/assistant/fake_assistant_settings_manager_impl.h
index 11c094f..dee5c159 100644
--- a/chromeos/services/assistant/fake_assistant_settings_manager_impl.h
+++ b/chromeos/services/assistant/fake_assistant_settings_manager_impl.h
@@ -30,8 +30,6 @@
       mojom::SpeakerIdEnrollmentClientPtr client) override;
   void StopSpeakerIdEnrollment(
       StopSpeakerIdEnrollmentCallback callback) override;
-  void RemoveSpeakerIdEnrollmentData(
-      RemoveSpeakerIdEnrollmentDataCallback callback) override;
 
   // AssistantSettingsManager overrides:
   void BindRequest(mojom::AssistantSettingsManagerRequest request) override;
diff --git a/chromeos/services/assistant/public/mojom/settings.mojom b/chromeos/services/assistant/public/mojom/settings.mojom
index 8d8836e..ace2615 100644
--- a/chromeos/services/assistant/public/mojom/settings.mojom
+++ b/chromeos/services/assistant/public/mojom/settings.mojom
@@ -41,7 +41,4 @@
 
   // Stops speaker id enrollment.
   StopSpeakerIdEnrollment() => ();
-
-  // Delete speaker id model.
-  RemoveSpeakerIdEnrollmentData() => ();
 };
diff --git a/components/arc/bluetooth/bluetooth_type_converters.cc b/components/arc/bluetooth/bluetooth_type_converters.cc
index e43217c..18cebd5d 100644
--- a/components/arc/bluetooth/bluetooth_type_converters.cc
+++ b/components/arc/bluetooth/bluetooth_type_converters.cc
@@ -10,6 +10,8 @@
 #include <string>
 #include <vector>
 
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -83,6 +85,14 @@
   auto result = arc::mojom::BluetoothSdpAttribute::New();
   result->type = attr_bluez.type();
   result->type_size = attr_bluez.size();
+
+  // TODO(b/111367421): Remove after migration.
+  if (result->type != bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE) {
+    std::string json;
+    base::JSONWriter::Write(attr_bluez.value(), &json);
+    result->json_value = std::move(json);
+  }
+
   switch (result->type) {
     case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE:
       result->value = base::Value();
@@ -117,6 +127,15 @@
               arc::mojom::BluetoothSdpAttributePtr>::
     Convert(const arc::mojom::BluetoothSdpAttributePtr& attr, size_t depth) {
   bluez::BluetoothServiceAttributeValueBlueZ::Type type = attr->type;
+
+  // TODO(b/111367421): Remove after migration.
+  if (type != bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE &&
+      attr->json_value.has_value()) {
+    return bluez::BluetoothServiceAttributeValueBlueZ(
+        type, static_cast<size_t>(attr->type_size),
+        base::JSONReader::Read(attr->json_value.value()));
+  }
+
   if (type != bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE &&
       !attr->value.has_value()) {
     return bluez::BluetoothServiceAttributeValueBlueZ();
diff --git a/components/autofill/content/renderer/prefilled_values_detector.cc b/components/autofill/content/renderer/prefilled_values_detector.cc
index c20059c..80290a4 100644
--- a/components/autofill/content/renderer/prefilled_values_detector.cc
+++ b/components/autofill/content/renderer/prefilled_values_detector.cc
@@ -21,7 +21,7 @@
                      "3~15个字符,中文字符7个以内",
                      "benutzername",
                      "client id",
-                     "codice titolare"
+                     "codice titolare",
                      "digite seu cpf ou e-mail",
                      "ds logon username",
                      "email",
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 466e4f1f..d44102b 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -85,6 +85,8 @@
 using ExpectedUkmMetrics =
     std::vector<std::vector<std::pair<const char*, int64_t>>>;
 
+const char* kTestGuid = "00000000-0000-0000-0000-000000000001";
+
 int64_t Collapse(uint64_t sig) {
   return sig % 1021;
 }
@@ -216,6 +218,14 @@
   }
 }
 
+void SetProfileTestData(AutofillProfile* profile) {
+  test::SetProfileInfo(profile, "Elvis", "Aaron", "Presley",
+                       "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
+                       "Apt. 10", "Memphis", "Tennessee", "38116", "US",
+                       "12345678901");
+  profile->set_guid(kTestGuid);
+}
+
 class MockAutofillClient : public TestAutofillClient {
  public:
   MockAutofillClient() {}
@@ -240,7 +250,8 @@
   void CreateAmbiguousProfiles();
 
   // Removes all existing profiles and creates one profile.
-  void RecreateProfile();
+  // |is_server| allows creation of |SERVER_PROFILE|.
+  void RecreateProfile(bool is_server);
 
   // Removes all existing credit cards and creates a local, masked server,
   // and/or full server credit card, according to the parameters.
@@ -351,16 +362,19 @@
   personal_data_->Refresh();
 }
 
-void AutofillMetricsTest::RecreateProfile() {
+void AutofillMetricsTest::RecreateProfile(bool is_server) {
   personal_data_->ClearProfiles();
 
-  AutofillProfile profile;
-  test::SetProfileInfo(&profile, "Elvis", "Aaron", "Presley",
-                       "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
-                       "Apt. 10", "Memphis", "Tennessee", "38116", "US",
-                       "12345678901");
-  profile.set_guid("00000000-0000-0000-0000-000000000001");
-  personal_data_->AddProfile(profile);
+  if (is_server) {
+    AutofillProfile profile(AutofillProfile::SERVER_PROFILE, "server_id");
+    SetProfileTestData(&profile);
+    personal_data_->AddProfile(profile);
+  } else {
+    AutofillProfile profile;
+    SetProfileTestData(&profile);
+    personal_data_->AddProfile(profile);
+  }
+
   personal_data_->Refresh();
 }
 
@@ -422,7 +436,7 @@
                        "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.",
                        "Apt. 10", "Memphis", "Tennessee", "38116", "US",
                        "12345678901");
-  profile1.set_guid("00000000-0000-0000-0000-000000000001");
+  profile1.set_guid(kTestGuid);
   personal_data_->AddProfile(profile1);
 
   AutofillProfile profile2;
@@ -721,7 +735,7 @@
   base::UserActionTester user_action_tester;
   // Simulate having seen this form on page load.
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-  std::string guid("00000000-0000-0000-0000-000000000001");
+  std::string guid(kTestGuid);
   // Trigger phone number rationalization at filling time.
   autofill_manager_->FillOrPreviewForm(
       AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -786,7 +800,7 @@
   base::UserActionTester user_action_tester;
   // Simulate having seen this form on page load.
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-  std::string guid("00000000-0000-0000-0000-000000000001");
+  std::string guid(kTestGuid);
   // Trigger phone number rationalization at filling time.
   autofill_manager_->FillOrPreviewForm(
       AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -813,7 +827,7 @@
 // correctly.
 TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
 
   // Set up our form data.
   FormData form;
@@ -868,7 +882,7 @@
   // Simulate filling form.
   {
     base::UserActionTester user_action_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile.
+    std::string guid(kTestGuid);  // local profile.
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -1240,7 +1254,7 @@
   base::UserActionTester user_action_tester;
   // Simulate having seen this form on page load.
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-  std::string guid("00000000-0000-0000-0000-000000000001");
+  std::string guid(kTestGuid);
   // Trigger phone number rationalization at filling time.
   autofill_manager_->FillOrPreviewForm(
       AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -1321,7 +1335,7 @@
   base::UserActionTester user_action_tester;
   // Simulate having seen this form on page load.
   autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
-  std::string guid("00000000-0000-0000-0000-000000000001");
+  std::string guid(kTestGuid);
   // Trigger phone number rationalization at filling time.
   autofill_manager_->FillOrPreviewForm(
       AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -3152,7 +3166,7 @@
 // Test that the profile checkout flow user actions are correctly logged.
 TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
 
   // Set up our form data.
   FormData form;
@@ -3207,7 +3221,7 @@
   // Simulate selecting a profile suggestions.
   {
     base::UserActionTester user_action_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile.
+    std::string guid(kTestGuid);  // local profile.
     external_delegate_->OnQuery(0, form, form.fields.front(), gfx::RectF());
     external_delegate_->DidAcceptSuggestion(
         ASCIIToUTF16("Test"),
@@ -3219,7 +3233,7 @@
   // Simulate filling a profile suggestion.
   {
     base::UserActionTester user_action_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile.
+    std::string guid(kTestGuid);  // local profile.
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -3412,7 +3426,7 @@
 // Tests that the Autofill_PolledProfileSuggestions user action is only logged
 // once if the field is queried repeatedly.
 TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) {
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
 
   // Set up the form data.
   FormData form;
@@ -4574,7 +4588,7 @@
 
 TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -5583,7 +5597,7 @@
 // Test that popup suppressed form events for address are logged.
 TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -5640,7 +5654,7 @@
 // Test that we log suggestion shown form events for address.
 TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -5729,7 +5743,7 @@
 // Test that we log filled form events for address.
 TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -5756,7 +5770,7 @@
   {
     // Simulating selecting/filling a local profile suggestion.
     base::HistogramTester histogram_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile
+    std::string guid(kTestGuid);  // local profile
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -5775,7 +5789,7 @@
   {
     // Simulating selecting/filling a local profile suggestion.
     base::HistogramTester histogram_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile
+    std::string guid(kTestGuid);  // local profile
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -5789,12 +5803,53 @@
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
   }
+
+  // Create a server profile and reset the autofill manager state.
+  RecreateProfile(/*is_server=*/true);
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulate selecting/filling a server profile suggestion.
+    base::HistogramTester histogram_tester;
+    std::string guid(kTestGuid);  // server profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(std::string(), guid));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+  }
+
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulate selecting/filling a server profile suggestion more than once.
+    base::HistogramTester histogram_tester;
+    std::string guid(kTestGuid);  // server profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(std::string(), guid));
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(std::string(), guid));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+  }
 }
 
 // Test that we log submitted form events for address.
 TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -5895,7 +5950,7 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(
         0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile
+    std::string guid(kTestGuid);  // local profile
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -6001,7 +6056,7 @@
 // Test that we log "will submit" and "submitted" form events for address.
 TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
   // Create a profile.
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -6069,7 +6124,7 @@
     base::HistogramTester histogram_tester;
     autofill_manager_->OnQueryFormFieldAutofill(
         0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
-    std::string guid("00000000-0000-0000-0000-000000000001");  // local profile
+    std::string guid(kTestGuid);  // local profile
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -6326,7 +6381,7 @@
   // Reset the autofill manager state.
   autofill_manager_->Reset();
   autofill_manager_->AddSeenForm(form, field_types, field_types);
-  RecreateProfile();
+  RecreateProfile(/*is_server=*/false);
 
   {
     // Simulate activating the autofill popup for the street field.
@@ -7115,7 +7170,7 @@
   // Simulate editing an autofilled field.
   {
     base::HistogramTester histogram_tester;
-    std::string guid("00000000-0000-0000-0000-000000000001");
+    std::string guid(kTestGuid);
     autofill_manager_->FillOrPreviewForm(
         AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
         autofill_manager_->MakeFrontendID(std::string(), guid));
@@ -8166,7 +8221,7 @@
   // Simulate seeing.
   base::HistogramTester histogram_tester;
   autofill_manager_->AddSeenForm(form, field_types, field_types);
-  std::string guid("00000000-0000-0000-0000-000000000001");
+  std::string guid(kTestGuid);
 
   // Simulate checking whether to fill a dynamic form before the form was filled
   // initially.
diff --git a/components/autofill_assistant_strings.grdp b/components/autofill_assistant_strings.grdp
index 1a140b1..45c36e3 100644
--- a/components/autofill_assistant_strings.grdp
+++ b/components/autofill_assistant_strings.grdp
@@ -7,7 +7,7 @@
     <message name="IDS_AUTOFILL_ASSISTANT_GIVE_UP" desc="Text label that is shown when autofill assistant cannot help anymore, because of a user action." formatter_data="android_java">
       I cannot help anymore, please continue on your own.
     </message>
-    <message name="IDS_AUTOFILL_ASSISTANT_MAYBE_GIVE_UP" desc="Text label shown next to an UNDO button when user action indicate they want to continue on their own." formatter_data="android_java">
+    <message name="IDS_AUTOFILL_ASSISTANT_MAYBE_GIVE_UP" desc="Text label shown next to an UNDO button when user action indicate they want to continue on their own.">
       Continue manually?
     </message>
     <message name="IDS_AUTOFILL_ASSISTANT_LOADING" desc="Text label that is shown during the loading of the first page, right after being triggered.">
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index a907750..0c159f1 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -916,6 +916,7 @@
     const std::string& bag_of_chips,
     bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!cache_guid.empty());
 
   // TODO(treib): Based on some crash reports, it seems like the user could have
   // signed out already at this point, so many of the steps below, including
@@ -1436,13 +1437,13 @@
   syncer::ConfigureContext configure_context;
   configure_context.authenticated_account_id =
       GetAuthenticatedAccountInfo().account_id;
-  configure_context.cache_guid = sync_client_->GetDeviceInfoSyncService()
-                                     ->GetLocalDeviceInfoProvider()
-                                     ->GetLocalSyncCacheGUID();
+  configure_context.cache_guid = sync_prefs_.GetCacheGuid();
   configure_context.storage_option = syncer::STORAGE_ON_DISK;
   configure_context.reason = reason;
   configure_context.configuration_start_time = base::Time::Now();
 
+  DCHECK(!configure_context.cache_guid.empty());
+
   if (!migrator_) {
     // We create the migrator at the same time.
     migrator_ = std::make_unique<syncer::BackendMigrator>(
@@ -2036,16 +2037,15 @@
 void ProfileSyncService::RemoveClientFromServer() const {
   if (!engine_initialized_)
     return;
-  const std::string cache_guid = sync_client_->GetDeviceInfoSyncService()
-                                     ->GetLocalDeviceInfoProvider()
-                                     ->GetLocalSyncCacheGUID();
+  const std::string cache_guid = sync_prefs_.GetCacheGuid();
+  DCHECK(!cache_guid.empty());
   std::string birthday;
   syncer::UserShare* user_share = GetUserShare();
   if (user_share && user_share->directory.get()) {
     birthday = user_share->directory->store_birthday();
   }
   const std::string& access_token = auth_manager_->access_token();
-  if (!access_token.empty() && !cache_guid.empty() && !birthday.empty()) {
+  if (!access_token.empty() && !birthday.empty()) {
     sync_stopped_reporter_->ReportSyncStopped(access_token, cache_guid,
                                               birthday);
   }
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc
index 253196a5..44684d2 100644
--- a/components/exo/surface_unittest.cc
+++ b/components/exo/surface_unittest.cc
@@ -77,7 +77,7 @@
 
 // Instantiate the Boolean which is used to toggle mouse and touch events in
 // the parameterized tests.
-INSTANTIATE_TEST_CASE_P(, SurfaceTest, testing::Values(1.0f, 1.25f, 2.0f));
+INSTANTIATE_TEST_SUITE_P(, SurfaceTest, testing::Values(1.0f, 1.25f, 2.0f));
 
 TEST_P(SurfaceTest, Attach) {
   gfx::Size buffer_size(256, 256);
diff --git a/components/exo/wayland/clients/perftests.cc b/components/exo/wayland/clients/perftests.cc
index 83ceab6e..52acea7 100644
--- a/components/exo/wayland/clients/perftests.cc
+++ b/components/exo/wayland/clients/perftests.cc
@@ -56,9 +56,9 @@
   DISALLOW_COPY_AND_ASSIGN(WaylandClientBlurPerfTests);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        WaylandClientBlurPerfTests,
-                        testing::Values(4.0, 15.0));
+INSTANTIATE_TEST_SUITE_P(,
+                         WaylandClientBlurPerfTests,
+                         testing::Values(4.0, 15.0));
 
 TEST_P(WaylandClientBlurPerfTests, BlurSigma) {
   const int kWarmUpFrames = 20;
diff --git a/components/favicon/content/content_favicon_driver.cc b/components/favicon/content/content_favicon_driver.cc
index be345ad..22443c6 100644
--- a/components/favicon/content/content_favicon_driver.cc
+++ b/components/favicon/content/content_favicon_driver.cc
@@ -141,7 +141,8 @@
     const gfx::Image& image) {
   content::NavigationEntry* entry =
       web_contents()->GetController().GetLastCommittedEntry();
-  DCHECK(entry && entry->GetURL() == page_url);
+  DCHECK(entry);
+  DCHECK_EQ(entry->GetURL(), page_url);
 
   if (notification_icon_type == FaviconDriverObserver::NON_TOUCH_16_DIP) {
     entry->GetFavicon().valid = true;
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index d46bcc43..77b9b50 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -104,8 +104,13 @@
   size_t slot = state_.AddrToSlot(state_.GetPageAddr(addr));
   DCHECK_EQ(addr, slots_[slot].alloc_ptr);
   // Check for double free.
-  if (slots_[slot].dealloc.trace_collected) {
+  if (slots_[slot].deallocation_occurred.exchange(true)) {
     state_.double_free_address = addr;
+    // TODO(https://crbug.com/925447): The other thread may not be done writing
+    // a stack trace so we could spin here until it's read; however, it's also
+    // possible we are racing an allocation in the middle of
+    // RecordAllocationInSlot. For now it's possible a racy double free could
+    // lead to a bad stack trace, but no internal allocator corruption.
     __builtin_trap();
   }
 
@@ -171,6 +176,7 @@
   slots_[slot].dealloc.tid = base::kInvalidThreadId;
   slots_[slot].dealloc.trace_len = 0;
   slots_[slot].dealloc.trace_collected = false;
+  slots_[slot].deallocation_occurred = false;
 }
 
 void GuardedPageAllocator::RecordDeallocationInSlot(size_t slot) {
diff --git a/components/gwp_asan/common/allocator_state.cc b/components/gwp_asan/common/allocator_state.cc
index cbe26aa..b7a455f 100644
--- a/components/gwp_asan/common/allocator_state.cc
+++ b/components/gwp_asan/common/allocator_state.cc
@@ -16,6 +16,8 @@
 constexpr size_t AllocatorState::kGpaMaxPages;
 constexpr size_t AllocatorState::kMaxStackFrames;
 
+AllocatorState::AllocatorState() {}
+
 AllocatorState::GetMetadataReturnType AllocatorState::GetMetadataForAddress(
     uintptr_t exception_address,
     uintptr_t* slot_address) const {
@@ -133,5 +135,7 @@
   return slot;
 }
 
+AllocatorState::SlotMetadata::SlotMetadata() {}
+
 }  // namespace internal
 }  // namespace gwp_asan
diff --git a/components/gwp_asan/common/allocator_state.h b/components/gwp_asan/common/allocator_state.h
index 3e053ea..7c69b9ef 100644
--- a/components/gwp_asan/common/allocator_state.h
+++ b/components/gwp_asan/common/allocator_state.h
@@ -25,6 +25,8 @@
 #ifndef COMPONENTS_GWP_ASAN_COMMON_ALLOCATOR_STATE_H_
 #define COMPONENTS_GWP_ASAN_COMMON_ALLOCATOR_STATE_H_
 
+#include <atomic>
+
 #include "base/threading/platform_thread.h"
 
 namespace gwp_asan {
@@ -55,6 +57,8 @@
 
   // Structure for storing data about a slot.
   struct SlotMetadata {
+    SlotMetadata();
+
     // Information saved for allocations and deallocations.
     struct AllocationInfo {
       // (De)allocation thread id or base::kInvalidThreadId if no (de)allocation
@@ -72,14 +76,15 @@
     size_t alloc_size = 0;
     // The allocation address.
     uintptr_t alloc_ptr = 0;
+    // Used to synchronize whether a deallocation has occurred (e.g. whether a
+    // double free has occurred) between threads.
+    std::atomic<bool> deallocation_occurred{false};
 
     AllocationInfo alloc;
     AllocationInfo dealloc;
   };
 
-  // TODO(vtsyrklevich): Get rid of inline (requires chromium-style plugin
-  // update.)
-  inline constexpr AllocatorState();
+  AllocatorState();
 
   // Returns true if address is in memory managed by this class.
   inline bool PointerIsMine(uintptr_t addr) const {
@@ -139,8 +144,6 @@
   DISALLOW_COPY_AND_ASSIGN(AllocatorState);
 };
 
-constexpr AllocatorState::AllocatorState() {}
-
 // Ensure that the allocator state is a plain-old-data. That way we can safely
 // initialize it by copying memory from out-of-process without worrying about
 // destructors operating on the fields in an unexpected way.
diff --git a/components/os_crypt/key_storage_kwallet_unittest.cc b/components/os_crypt/key_storage_kwallet_unittest.cc
index 9f9b7da..2525d210 100644
--- a/components/os_crypt/key_storage_kwallet_unittest.cc
+++ b/components/os_crypt/key_storage_kwallet_unittest.cc
@@ -262,9 +262,9 @@
   DISALLOW_COPY_AND_ASSIGN(KeyStorageKWalletFailuresTest);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        KeyStorageKWalletFailuresTest,
-                        ::testing::Values(CANNOT_READ, CANNOT_CONTACT));
+INSTANTIATE_TEST_SUITE_P(,
+                         KeyStorageKWalletFailuresTest,
+                         ::testing::Values(CANNOT_READ, CANNOT_CONTACT));
 
 TEST_P(KeyStorageKWalletFailuresTest, PostInitFailureOpen) {
   EXPECT_CALL(*kwallet_dbus_mock_, Open(_, _, _)).WillOnce(Return(GetParam()));
diff --git a/components/os_crypt/kwallet_dbus_unittest.cc b/components/os_crypt/kwallet_dbus_unittest.cc
index 53efb76..e5c6e60 100644
--- a/components/os_crypt/kwallet_dbus_unittest.cc
+++ b/components/os_crypt/kwallet_dbus_unittest.cc
@@ -119,10 +119,11 @@
   DISALLOW_COPY_AND_ASSIGN(KWalletDBusTest);
 };
 
-INSTANTIATE_TEST_CASE_P(,
-                        KWalletDBusTest,
-                        ::testing::Values(base::nix::DESKTOP_ENVIRONMENT_KDE4,
-                                          base::nix::DESKTOP_ENVIRONMENT_KDE5));
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    KWalletDBusTest,
+    ::testing::Values(base::nix::DESKTOP_ENVIRONMENT_KDE4,
+                      base::nix::DESKTOP_ENVIRONMENT_KDE5));
 
 // Matches a method call to the specified dbus target.
 MATCHER_P2(Calls, interface, member, "") {
diff --git a/components/signin/core/browser/signin_manager_unittest.cc b/components/signin/core/browser/signin_manager_unittest.cc
index 5cb99d8..145a959 100644
--- a/components/signin/core/browser/signin_manager_unittest.cc
+++ b/components/signin/core/browser/signin_manager_unittest.cc
@@ -77,7 +77,9 @@
   SigninManagerTest()
       : test_signin_client_(&user_prefs_),
         token_service_(&user_prefs_),
-        cookie_manager_service_(&token_service_, &test_signin_client_),
+        cookie_manager_service_(&token_service_,
+                                &test_signin_client_,
+                                &test_url_loader_factory_),
         account_consistency_(signin::AccountConsistencyMethod::kDisabled) {
     AccountFetcherService::RegisterPrefs(user_prefs_.registry());
     AccountTrackerService::RegisterPrefs(user_prefs_.registry());
@@ -160,7 +162,8 @@
   TestSigninClient test_signin_client_;
   FakeProfileOAuth2TokenService token_service_;
   AccountTrackerService account_tracker_;
-  GaiaCookieManagerService cookie_manager_service_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  FakeGaiaCookieManagerService cookie_manager_service_;
   FakeAccountFetcherService account_fetcher_;
   std::unique_ptr<SigninManager> manager_;
   TestSigninManagerObserver test_observer_;
diff --git a/components/sync/device_info/local_device_info_provider.h b/components/sync/device_info/local_device_info_provider.h
index f98de96..fdd3788 100644
--- a/components/sync/device_info/local_device_info_provider.h
+++ b/components/sync/device_info/local_device_info_provider.h
@@ -36,13 +36,6 @@
   // classifying client types when calculating statistics.
   virtual std::string GetSyncUserAgent() const = 0;
 
-  // Returns a GUID string used for creation of the machine tag for
-  // this local session; an empty sting if LocalDeviceInfoProvider hasn't been
-  // initialized yet.
-  // TODO(crbug.com/922971): Remove this function since it is redundant with the
-  // cache GUID exposed via GetLocalDeviceInfo().
-  virtual std::string GetLocalSyncCacheGUID() const = 0;
-
   // Registers a callback to be called when local device info becomes available.
   // The callback will remain registered until the
   // returned Subscription is destroyed, which must occur before the
diff --git a/components/sync/device_info/local_device_info_provider_impl.cc b/components/sync/device_info/local_device_info_provider_impl.cc
index a92b9cd9..540e341b 100644
--- a/components/sync/device_info/local_device_info_provider_impl.cc
+++ b/components/sync/device_info/local_device_info_provider_impl.cc
@@ -63,11 +63,6 @@
   return MakeUserAgentForSync(channel_, is_tablet_);
 }
 
-std::string LocalDeviceInfoProviderImpl::GetLocalSyncCacheGUID() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return cache_guid_;
-}
-
 std::unique_ptr<LocalDeviceInfoProvider::Subscription>
 LocalDeviceInfoProviderImpl::RegisterOnInitializedCallback(
     const base::RepeatingClosure& callback) {
@@ -80,7 +75,6 @@
                                              const std::string& session_name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!cache_guid.empty());
-  cache_guid_ = cache_guid;
 
   local_device_info_ = std::make_unique<DeviceInfo>(
       cache_guid, session_name, version_, GetSyncUserAgent(),
@@ -92,7 +86,6 @@
 
 void LocalDeviceInfoProviderImpl::Clear() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  cache_guid_ = "";
   local_device_info_.reset();
 }
 
diff --git a/components/sync/device_info/local_device_info_provider_impl.h b/components/sync/device_info/local_device_info_provider_impl.h
index 71596b32..92688a5 100644
--- a/components/sync/device_info/local_device_info_provider_impl.h
+++ b/components/sync/device_info/local_device_info_provider_impl.h
@@ -34,7 +34,6 @@
   version_info::Channel GetChannel() const override;
   const DeviceInfo* GetLocalDeviceInfo() const override;
   std::string GetSyncUserAgent() const override;
-  std::string GetLocalSyncCacheGUID() const override;
   std::unique_ptr<Subscription> RegisterOnInitializedCallback(
       const base::RepeatingClosure& callback) override;
 
@@ -55,7 +54,6 @@
 
   const SigninScopedDeviceIdCallback signin_scoped_device_id_callback_;
 
-  std::string cache_guid_;
   std::unique_ptr<DeviceInfo> local_device_info_;
   base::CallbackList<void(void)> callback_list_;
 
diff --git a/components/sync/device_info/local_device_info_provider_impl_unittest.cc b/components/sync/device_info/local_device_info_provider_impl_unittest.cc
index ea808ea..b1ae67bb 100644
--- a/components/sync/device_info/local_device_info_provider_impl_unittest.cc
+++ b/components/sync/device_info/local_device_info_provider_impl_unittest.cc
@@ -76,15 +76,5 @@
             provider_->GetLocalDeviceInfo()->signin_scoped_device_id());
 }
 
-TEST_F(LocalDeviceInfoProviderImplTest, GetLocalSyncCacheGUID) {
-  EXPECT_TRUE(provider_->GetLocalSyncCacheGUID().empty());
-
-  InitializeProvider();
-  EXPECT_EQ(std::string(kLocalDeviceGuid), provider_->GetLocalSyncCacheGUID());
-
-  provider_->Clear();
-  EXPECT_TRUE(provider_->GetLocalSyncCacheGUID().empty());
-}
-
 }  // namespace
 }  // namespace syncer
diff --git a/components/sync/device_info/local_device_info_provider_mock.cc b/components/sync/device_info/local_device_info_provider_mock.cc
index c16872e21..e24deb2 100644
--- a/components/sync/device_info/local_device_info_provider_mock.cc
+++ b/components/sync/device_info/local_device_info_provider_mock.cc
@@ -37,10 +37,6 @@
   return "useragent";
 }
 
-std::string LocalDeviceInfoProviderMock::GetLocalSyncCacheGUID() const {
-  return local_device_info_ ? local_device_info_->guid() : "";
-}
-
 void LocalDeviceInfoProviderMock::Initialize(const std::string& cache_guid,
                                              const std::string& session_name) {
   local_device_info_ = std::make_unique<DeviceInfo>(
diff --git a/components/sync/device_info/local_device_info_provider_mock.h b/components/sync/device_info/local_device_info_provider_mock.h
index d29519e3..0586534f 100644
--- a/components/sync/device_info/local_device_info_provider_mock.h
+++ b/components/sync/device_info/local_device_info_provider_mock.h
@@ -37,7 +37,6 @@
   version_info::Channel GetChannel() const override;
   const DeviceInfo* GetLocalDeviceInfo() const override;
   std::string GetSyncUserAgent() const override;
-  std::string GetLocalSyncCacheGUID() const override;
   std::unique_ptr<Subscription> RegisterOnInitializedCallback(
       const base::Closure& callback) override;
 
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 7156c30..90af92ec 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -69,6 +69,7 @@
       "//base",
       "//gpu/vulkan",
       "//gpu/vulkan:buildflags",
+      "//ui/gfx",
     ]
   }
 }
diff --git a/components/viz/common/gpu/vulkan_in_process_context_provider.cc b/components/viz/common/gpu/vulkan_in_process_context_provider.cc
index 74d4461..581794b8 100644
--- a/components/viz/common/gpu/vulkan_in_process_context_provider.cc
+++ b/components/viz/common/gpu/vulkan_in_process_context_provider.cc
@@ -7,6 +7,7 @@
 #include "gpu/vulkan/vulkan_device_queue.h"
 #include "gpu/vulkan/vulkan_function_pointers.h"
 #include "gpu/vulkan/vulkan_implementation.h"
+#include "gpu/vulkan/vulkan_instance.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
 namespace viz {
@@ -50,14 +51,25 @@
   backend_context.fDevice = device_queue_->GetVulkanDevice();
   backend_context.fQueue = device_queue_->GetVulkanQueue();
   backend_context.fGraphicsQueueIndex = device_queue_->GetVulkanQueueIndex();
+  backend_context.fInstanceVersion =
+      vulkan_implementation_->GetVulkanInstance()->api_version();
 
-  // gpu::VulkanInstance always initializes apiVersion=1.1.
-  // TODO(sergeyu): Extend VulkanImplementation interface to provide apiVersion
-  // and list of enabled extensions instead of hardcoding them here.
-  backend_context.fInstanceVersion = VK_MAKE_VERSION(1, 1, 0);
-  backend_context.fExtensions =
-      kEXT_debug_report_GrVkExtensionFlag | kKHR_surface_GrVkExtensionFlag |
-      kKHR_swapchain_GrVkExtensionFlag | kKHR_xcb_surface_GrVkExtensionFlag;
+  backend_context.fExtensions = 0;
+
+  // Instance extensions.
+  const gfx::ExtensionSet& extensions =
+      vulkan_implementation_->GetVulkanInstance()->enabled_extensions();
+  if (gfx::HasExtension(extensions, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
+    backend_context.fExtensions |= kEXT_debug_report_GrVkExtensionFlag;
+  if (gfx::HasExtension(extensions, VK_KHR_SURFACE_EXTENSION_NAME))
+    backend_context.fExtensions |= kKHR_surface_GrVkExtensionFlag;
+
+  // Device extensions.
+  if (gfx::HasExtension(device_queue_->enabled_extensions(),
+                        VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+    backend_context.fExtensions |= kKHR_swapchain_GrVkExtensionFlag;
+  }
+
   backend_context.fFeatures = kGeometryShader_GrVkFeatureFlag |
                               kDualSrcBlend_GrVkFeatureFlag |
                               kSampleRateShading_GrVkFeatureFlag;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 7083ee6a..d79cb19 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -667,7 +667,9 @@
   auto* image = texture->GetLevelImage(GL_TEXTURE_2D, 0, &image_state);
   if (image && image_state == gpu::gles2::Texture::UNBOUND) {
     glBindTexture(texture_base->target(), texture_base->service_id());
-    if (image->BindTexImage(texture_base->target())) {
+    if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+      if (!image->BindTexImage(texture_base->target()))
+        LOG(ERROR) << "Failed to bind a gl image to texture.";
     } else {
       texture->SetLevelImageState(texture_base->target(), 0,
                                   gpu::gles2::Texture::COPIED);
diff --git a/content/browser/devtools/protocol/target_auto_attacher.cc b/content/browser/devtools/protocol/target_auto_attacher.cc
index 634b02a..30a25805 100644
--- a/content/browser/devtools/protocol/target_auto_attacher.cc
+++ b/content/browser/devtools/protocol/target_auto_attacher.cc
@@ -11,6 +11,7 @@
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 
 namespace content {
@@ -84,6 +85,37 @@
   return result;
 }
 
+base::flat_set<GURL> GetFrameUrls(RenderFrameHostImpl* render_frame_host) {
+  // We try to attach to a service worker in the following cases:
+  // 1. SW is created while user is inspecting frame (from WorkerCreated).
+  // 2. Frame has navigated and we are picking up new SW corresponding to new
+  //    url (from DidFinishNavigation).
+  // 3. Frame is trying to navigate and it spawns a new SW which we pick up
+  //    (from WorkerCreated). See also https://crbug.com/907072
+  //
+  // We are not attaching in the following case:
+  // 4. Frame is trying to navigate and we _should_ pick up an existing SW but we don't.
+  //    We _could_ do this, but since we are not pausing the navigation, there
+  //    is no principal difference between picking up SW earlier or later.
+  //
+  // We also try to detach from SW picked up for [3] if navigation has failed
+  // (from DidFinishNavigation).
+
+  base::flat_set<GURL> frame_urls;
+  if (render_frame_host) {
+    for (FrameTreeNode* node :
+         render_frame_host->frame_tree_node()->frame_tree()->Nodes()) {
+      frame_urls.insert(node->current_url());
+      // We use both old and new frame urls to support [3], where we attach while
+      // navigation is still ongoing.
+      if (node->navigation_request()) {
+        frame_urls.insert(node->navigation_request()->common_params().url);
+      }
+    }
+  }
+  return frame_urls;
+}
+
 }  // namespace
 
 TargetAutoAttacher::TargetAutoAttacher(
@@ -186,17 +218,12 @@
   if (!auto_attaching_service_workers_)
     return;
 
-  frame_urls_.clear();
   BrowserContext* browser_context = nullptr;
-  if (render_frame_host_) {
-    for (FrameTreeNode* node :
-         render_frame_host_->frame_tree_node()->frame_tree()->Nodes()) {
-      frame_urls_.insert(node->current_url());
-    }
+  if (render_frame_host_)
     browser_context = render_frame_host_->GetProcess()->GetBrowserContext();
-  }
 
-  auto matching = GetMatchingServiceWorkers(browser_context, frame_urls_);
+  auto matching = GetMatchingServiceWorkers(browser_context,
+                                            GetFrameUrls(render_frame_host_));
   Hosts new_hosts;
   for (const auto& pair : matching)
     new_hosts.insert(pair.second);
@@ -260,7 +287,9 @@
   BrowserContext* browser_context = nullptr;
   if (render_frame_host_)
     browser_context = render_frame_host_->GetProcess()->GetBrowserContext();
-  auto hosts = GetMatchingServiceWorkers(browser_context, frame_urls_);
+
+  auto hosts = GetMatchingServiceWorkers(browser_context,
+                                         GetFrameUrls(render_frame_host_));
   if (hosts.find(host->GetId()) != hosts.end()) {
     *should_pause_on_start = wait_for_debugger_on_start_;
     Hosts new_hosts;
diff --git a/content/browser/devtools/protocol/target_auto_attacher.h b/content/browser/devtools/protocol/target_auto_attacher.h
index cf32304b..4e49d3d 100644
--- a/content/browser/devtools/protocol/target_auto_attacher.h
+++ b/content/browser/devtools/protocol/target_auto_attacher.h
@@ -64,7 +64,6 @@
   DetachCallback detach_callback_;
   DevToolsRendererChannel* renderer_channel_;
   RenderFrameHostImpl* render_frame_host_;
-  base::flat_set<GURL> frame_urls_;
 
   bool auto_attach_;
   bool wait_for_debugger_on_start_;
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index 509a7c9..708fd40 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -465,7 +465,7 @@
   return Response::OK();
 }
 
-void TargetHandler::DidCommitNavigation() {
+void TargetHandler::DidFinishNavigation() {
   auto_attacher_.UpdateServiceWorkers();
 }
 
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h
index fc1d41d1..514a59f6 100644
--- a/content/browser/devtools/protocol/target_handler.h
+++ b/content/browser/devtools/protocol/target_handler.h
@@ -53,7 +53,7 @@
                    RenderFrameHostImpl* frame_host) override;
   Response Disable() override;
 
-  void DidCommitNavigation();
+  void DidFinishNavigation();
   std::unique_ptr<NavigationThrottle> CreateThrottleForNavigation(
       NavigationHandle* navigation_handle);
 
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 37974ae..cade752 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -404,10 +404,8 @@
     for (DevToolsSession* session : sessions())
       session->ResumeSendingMessagesToAgent();
   }
-  if (handle->HasCommitted()) {
-    for (auto* target : protocol::TargetHandler::ForAgentHost(this))
-      target->DidCommitNavigation();
-  }
+  for (auto* target : protocol::TargetHandler::ForAgentHost(this))
+    target->DidFinishNavigation();
 }
 
 void RenderFrameDevToolsAgentHost::UpdateFrameHost(
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc
index 988809b..26a1fb9 100644
--- a/content/browser/find_request_manager_browsertest.cc
+++ b/content/browser/find_request_manager_browsertest.cc
@@ -142,8 +142,9 @@
   DISALLOW_COPY_AND_ASSIGN(FindRequestManagerTest);
 };
 
-INSTANTIATE_TEST_CASE_P(
-    FindRequestManagerTests, FindRequestManagerTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(FindRequestManagerTests,
+                         FindRequestManagerTest,
+                         testing::Bool());
 
 // TODO(crbug.com/615291): These tests frequently fail on Android.
 #if defined(OS_ANDROID)
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index 75e014e8..6dddb58 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -176,26 +176,26 @@
 using ::testing::Combine;
 using ::testing::Values;
 
-INSTANTIATE_TEST_CASE_P(SRC_ClearKey,
-                        EncryptedMediaTest,
-                        Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::SRC)));
+INSTANTIATE_TEST_SUITE_P(SRC_ClearKey,
+                         EncryptedMediaTest,
+                         Combine(Values(kClearKeyKeySystem),
+                                 Values(SrcType::SRC)));
 
-INSTANTIATE_TEST_CASE_P(MSE_ClearKey,
-                        EncryptedMediaTest,
-                        Combine(Values(kClearKeyKeySystem),
-                                Values(SrcType::MSE)));
+INSTANTIATE_TEST_SUITE_P(MSE_ClearKey,
+                         EncryptedMediaTest,
+                         Combine(Values(kClearKeyKeySystem),
+                                 Values(SrcType::MSE)));
 
 #if defined(SUPPORTS_EXTERNAL_CLEAR_KEY_IN_CONTENT_SHELL)
-INSTANTIATE_TEST_CASE_P(SRC_ExternalClearKey,
-                        EncryptedMediaTest,
-                        Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::SRC)));
+INSTANTIATE_TEST_SUITE_P(SRC_ExternalClearKey,
+                         EncryptedMediaTest,
+                         Combine(Values(kExternalClearKeyKeySystem),
+                                 Values(SrcType::SRC)));
 
-INSTANTIATE_TEST_CASE_P(MSE_ExternalClearKey,
-                        EncryptedMediaTest,
-                        Combine(Values(kExternalClearKeyKeySystem),
-                                Values(SrcType::MSE)));
+INSTANTIATE_TEST_SUITE_P(MSE_ExternalClearKey,
+                         EncryptedMediaTest,
+                         Combine(Values(kExternalClearKeyKeySystem),
+                                 Values(SrcType::MSE)));
 #endif
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_WebM) {
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc
index 5aabc37..8c47e38 100644
--- a/content/browser/media/media_browsertest.cc
+++ b/content/browser/media/media_browsertest.cc
@@ -328,7 +328,7 @@
   EXPECT_FALSE(shell()->web_contents()->IsCrashed());
 }
 
-INSTANTIATE_TEST_CASE_P(File, MediaTest, ::testing::Values(false));
-INSTANTIATE_TEST_CASE_P(Http, MediaTest, ::testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(File, MediaTest, ::testing::Values(false));
+INSTANTIATE_TEST_SUITE_P(Http, MediaTest, ::testing::Values(true));
 
 }  // namespace content
diff --git a/content/browser/media/media_capabilities_browsertest.cc b/content/browser/media/media_capabilities_browsertest.cc
index 3c3e816..88d89ab8 100644
--- a/content/browser/media/media_capabilities_browsertest.cc
+++ b/content/browser/media/media_capabilities_browsertest.cc
@@ -229,9 +229,9 @@
             CanDecodeAudio(config_type, "'audio/ogg; codecs=\"vorbis\"'"));
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        MediaCapabilitiesTestWithConfigType,
-                        ::testing::Values(ConfigType::kFile,
-                                          ConfigType::kMediaSource));
+INSTANTIATE_TEST_SUITE_P(,
+                         MediaCapabilitiesTestWithConfigType,
+                         ::testing::Values(ConfigType::kFile,
+                                           ConfigType::kMediaSource));
 
 }  // namespace content
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc
index 632cd03..1880e9af 100644
--- a/content/browser/media/media_internals_unittest.cc
+++ b/content/browser/media/media_internals_unittest.cc
@@ -306,7 +306,7 @@
   ExpectStatus("closed");
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     MediaInternalsAudioLogTest,
     MediaInternalsAudioLogTest,
     testing::Values(media::AudioLogFactory::AUDIO_INPUT_CONTROLLER,
diff --git a/content/browser/renderer_host/code_cache_host_impl.cc b/content/browser/renderer_host/code_cache_host_impl.cc
index 34c9ed4..63a98701 100644
--- a/content/browser/renderer_host/code_cache_host_impl.cc
+++ b/content/browser/renderer_host/code_cache_host_impl.cc
@@ -22,7 +22,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
-#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/features.h"
 #include "net/base/io_buffer.h"
@@ -203,12 +202,6 @@
     const std::vector<uint8_t>& data,
     const url::Origin& cache_storage_origin,
     const std::string& cache_storage_cache_name) {
-  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanAccessDataForOrigin(
-          render_process_id_, cache_storage_origin.GetURL())) {
-    mojo::ReportBadMessage("CODE_CACHE_INVALID_CACHE_STORAGE_ORIGIN");
-    return;
-  }
-
   if (!cache_storage_context_->cache_manager())
     return;
 
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index e61ffb2..6c13e1a 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -137,9 +137,16 @@
     return FilterGestureEventResult::kFilterGestureEventDelayed;
   }
 
-  base::Optional<cc::TouchAction> touch_action =
-      active_touch_action_.has_value() ? active_touch_action_
-                                       : white_listed_touch_action_;
+  base::Optional<cc::TouchAction> touch_action;
+  if (gesture_event->GetType() == WebInputEvent::kGestureTapDown) {
+    touch_action = allowed_touch_action_.has_value()
+                       ? allowed_touch_action_
+                       : white_listed_touch_action_;
+  } else {
+    touch_action = active_touch_action_.has_value()
+                       ? active_touch_action_
+                       : white_listed_touch_action_;
+  }
 
   // Filter for allowable touch actions first (eg. before the TouchEventQueue
   // can decide to send a touch cancel event).
diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
index 57773ccc..d00a39c 100644
--- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -293,6 +293,10 @@
       *media_observer_,
       OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
                                  MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
+                                 MEDIA_REQUEST_STATE_CLOSING));
   media_stream_manager_->CancelRequest(label);
   run_loop_.RunUntilIdle();
 }
@@ -355,6 +359,10 @@
       *media_observer_,
       OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_VIDEO_CAPTURE,
                                  MEDIA_REQUEST_STATE_CLOSING));
+  EXPECT_CALL(
+      *media_observer_,
+      OnMediaRequestStateChanged(_, _, _, _, blink::MEDIA_DISPLAY_AUDIO_CAPTURE,
+                                 MEDIA_REQUEST_STATE_CLOSING));
 
   media_stream_manager_->CancelRequest(label1);
 
diff --git a/content/browser/speech/mock_tts_controller.cc b/content/browser/speech/mock_tts_controller.cc
index cf7fb8a..0b98dfe 100644
--- a/content/browser/speech/mock_tts_controller.cc
+++ b/content/browser/speech/mock_tts_controller.cc
@@ -37,6 +37,7 @@
   void OnTtsEvent(int utterance_id,
                   TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override {}
 
   void GetVoices(BrowserContext* browser_context,
@@ -69,4 +70,4 @@
   return MockTtsController::GetInstance();
 }
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/speech/tts_android.cc b/content/browser/speech/tts_android.cc
index dce7244..706251d 100644
--- a/content/browser/speech/tts_android.cc
+++ b/content/browser/speech/tts_android.cc
@@ -116,7 +116,7 @@
     return;
 
   TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
-                                           std::string());
+                                           utterance_.size(), std::string());
 }
 
 void TtsPlatformImplAndroid::SendFinalTtsEvent(int utterance_id,
@@ -126,7 +126,7 @@
     return;
 
   TtsController::GetInstance()->OnTtsEvent(utterance_id_, event_type,
-                                           char_index, std::string());
+                                           char_index, -1, std::string());
   utterance_id_ = 0;
   utterance_.clear();
 }
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 8327146..c47dac1f 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -23,6 +23,9 @@
 // A value to be used to indicate that there is no char index available.
 const int kInvalidCharIndex = -1;
 
+// A value to be used to indicate that there is no length available.
+const int kInvalidLength = -1;
+
 //
 // VoiceData
 //
@@ -119,7 +122,7 @@
 
   if (current_utterance_)
     current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
-                                   std::string());
+                                   kInvalidLength, std::string());
   FinishCurrentUtterance();
   ClearUtteranceQueue(true);  // Send events.
 }
@@ -157,6 +160,7 @@
 void TtsControllerImpl::OnTtsEvent(int utterance_id,
                                    TtsEventType event_type,
                                    int char_index,
+                                   int length,
                                    const std::string& error_message) {
   // We may sometimes receive completion callbacks "late", after we've
   // already finished the utterance (for example because another utterance
@@ -205,7 +209,7 @@
   UMA_HISTOGRAM_ENUMERATION("TextToSpeech.Event", metric,
                             UMATextToSpeechEvent::COUNT);
 
-  current_utterance_->OnTtsEvent(event_type, char_index, error_message);
+  current_utterance_->OnTtsEvent(event_type, char_index, length, error_message);
   if (current_utterance_->IsFinished()) {
     FinishCurrentUtterance();
     SpeakNextUtterance();
@@ -390,7 +394,7 @@
     }
 
     if (!success) {
-      utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex,
+      utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex, kInvalidLength,
                             GetTtsPlatform()->GetError());
       delete utterance;
       return;
@@ -404,7 +408,7 @@
     utterance_queue_.pop();
     if (send_events)
       utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex,
-                            std::string());
+                            kInvalidLength, std::string());
     else
       utterance->Finish();
     delete utterance;
@@ -415,7 +419,7 @@
   if (current_utterance_) {
     if (!current_utterance_->IsFinished())
       current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
-                                     std::string());
+                                     kInvalidLength, std::string());
     delete current_utterance_;
     current_utterance_ = nullptr;
   }
diff --git a/content/browser/speech/tts_controller_impl.h b/content/browser/speech/tts_controller_impl.h
index fb16907..d959a6c 100644
--- a/content/browser/speech/tts_controller_impl.h
+++ b/content/browser/speech/tts_controller_impl.h
@@ -47,6 +47,7 @@
   void OnTtsEvent(int utterance_id,
                   TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override;
   void GetVoices(BrowserContext* browser_context,
                  std::vector<VoiceData>* out_voices) override;
diff --git a/content/browser/speech/tts_linux.cc b/content/browser/speech/tts_linux.cc
index 5803a5f..e8ec9a37 100644
--- a/content/browser/speech/tts_linux.cc
+++ b/content/browser/speech/tts_linux.cc
@@ -279,25 +279,29 @@
   TtsController* controller = TtsController::GetInstance();
   switch (type) {
     case SPD_EVENT_BEGIN:
-      controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
+      controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
+                             utterance_.size(), std::string());
       break;
     case SPD_EVENT_RESUME:
-      controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
+      controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, -1,
+                             std::string());
       break;
     case SPD_EVENT_END:
-      controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(),
+      controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(), 0,
                              std::string());
       break;
     case SPD_EVENT_PAUSE:
       controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
-                             std::string());
+                             -1, std::string());
       break;
     case SPD_EVENT_CANCEL:
-      controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0,
+      controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0, -1,
                              std::string());
       break;
     case SPD_EVENT_INDEX_MARK:
-      controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
+      // TODO: Can we get length from linux?
+      controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, -1,
+                             std::string());
       break;
   }
 }
diff --git a/content/browser/speech/tts_mac.mm b/content/browser/speech/tts_mac.mm
index 0ef1f02c..90bd14f 100644
--- a/content/browser/speech/tts_mac.mm
+++ b/content/browser/speech/tts_mac.mm
@@ -163,7 +163,7 @@
   bool success = [speech_synthesizer_ startSpeakingRetainedUtterance];
   if (success) {
     content::TtsController* controller = content::TtsController::GetInstance();
-    controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_START, 0, "");
+    controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_START, 0, -1, "");
   }
   return success;
 }
@@ -182,7 +182,7 @@
     [speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary];
     paused_ = true;
     content::TtsController::GetInstance()->OnTtsEvent(
-        utterance_id_, content::TTS_EVENT_PAUSE, last_char_index_, "");
+        utterance_id_, content::TTS_EVENT_PAUSE, last_char_index_, -1, "");
   }
 }
 
@@ -191,7 +191,7 @@
     [speech_synthesizer_ continueSpeaking];
     paused_ = false;
     content::TtsController::GetInstance()->OnTtsEvent(
-        utterance_id_, content::TTS_EVENT_RESUME, last_char_index_, "");
+        utterance_id_, content::TTS_EVENT_RESUME, last_char_index_, -1, "");
   }
 }
 
@@ -265,8 +265,9 @@
   if (event_type == content::TTS_EVENT_END)
     char_index = utterance_.size();
 
-  content::TtsController::GetInstance()->OnTtsEvent(utterance_id_, event_type,
-                                                    char_index, error_message);
+  // TODO: Use mac's word length here.
+  content::TtsController::GetInstance()->OnTtsEvent(
+      utterance_id_, event_type, char_index, -1, error_message);
   last_char_index_ = char_index;
 }
 
@@ -349,4 +350,4 @@
   return false;
 }
 
-@end
\ No newline at end of file
+@end
diff --git a/content/browser/speech/tts_utterance_impl.cc b/content/browser/speech/tts_utterance_impl.cc
index 1c57bc0d..8aae27b8b 100644
--- a/content/browser/speech/tts_utterance_impl.cc
+++ b/content/browser/speech/tts_utterance_impl.cc
@@ -57,6 +57,7 @@
 
 void TtsUtteranceImpl::OnTtsEvent(TtsEventType event_type,
                                   int char_index,
+                                  int length,
                                   const std::string& error_message) {
   if (char_index >= 0)
     char_index_ = char_index;
@@ -64,7 +65,8 @@
     finished_ = true;
 
   if (event_delegate_)
-    event_delegate_->OnTtsEvent(this, event_type, char_index, error_message);
+    event_delegate_->OnTtsEvent(this, event_type, char_index, length,
+                                error_message);
   if (finished_)
     event_delegate_ = nullptr;
 }
diff --git a/content/browser/speech/tts_utterance_impl.h b/content/browser/speech/tts_utterance_impl.h
index d8b4fa4..c57c964 100644
--- a/content/browser/speech/tts_utterance_impl.h
+++ b/content/browser/speech/tts_utterance_impl.h
@@ -24,6 +24,7 @@
   // TtsUtterance overrides.
   void OnTtsEvent(TtsEventType event_type,
                   int char_index,
+                  int length,
                   const std::string& error_message) override;
 
   void Finish() override;
@@ -120,4 +121,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SPEECH_TTS_UTTERANCE_IMPL_H_
\ No newline at end of file
+#endif  // CONTENT_BROWSER_SPEECH_TTS_UTTERANCE_IMPL_H_
diff --git a/content/browser/speech/tts_win.cc b/content/browser/speech/tts_win.cc
index 2430fcc..78d1a58 100644
--- a/content/browser/speech/tts_win.cc
+++ b/content/browser/speech/tts_win.cc
@@ -156,7 +156,7 @@
     speech_synthesizer_->Pause();
     paused_ = true;
     TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
-                                             char_position_, "");
+                                             char_position_, -1, "");
   }
 }
 
@@ -165,7 +165,7 @@
     speech_synthesizer_->Resume();
     paused_ = false;
     TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
-                                             char_position_, "");
+                                             char_position_, -1, "");
   }
 }
 
@@ -239,27 +239,28 @@
 
     switch (event.eEventId) {
       case SPEI_START_INPUT_STREAM:
-        controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
+        controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, -1,
                                std::string());
         break;
       case SPEI_END_INPUT_STREAM:
         char_position_ = utterance_.size();
-        controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_,
+        controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_, 0,
                                std::string());
         break;
       case SPEI_TTS_BOOKMARK:
         controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, char_position_,
-                               std::string());
+                               -1, std::string());
         break;
       case SPEI_WORD_BOUNDARY:
         char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
+        // TODO: Get length of win word from win specific tts things.
         controller->OnTtsEvent(utterance_id_, TTS_EVENT_WORD, char_position_,
-                               std::string());
+                               -1, std::string());
         break;
       case SPEI_SENTENCE_BOUNDARY:
         char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
         controller->OnTtsEvent(utterance_id_, TTS_EVENT_SENTENCE,
-                               char_position_, std::string());
+                               char_position_, -1, std::string());
         break;
       default:
         break;
@@ -326,4 +327,4 @@
   GetInstance()->OnSpeechEvent();
 }
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index cad21e1a..e4401765 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -726,7 +726,7 @@
     case BackgroundTracingConfigImpl::CategoryPreset::BENCHMARK_NAVIGATION: {
       auto config = TraceConfig(
           "benchmark,toplevel,ipc,base,browser,navigation,omnibox,ui,shutdown,"
-          "safe_browsing,Java,EarlyJava,loading,startup,mojom,"
+          "safe_browsing,Java,EarlyJava,loading,startup,mojom,renderer_host,"
           "disabled-by-default-system_stats,disabled-by-default-cpu_profiler",
           record_mode);
       // Filter only browser process events.
@@ -738,6 +738,7 @@
     case BackgroundTracingConfigImpl::CategoryPreset::BENCHMARK_RENDERERS:
       return TraceConfig(
           "benchmark,toplevel,ipc,base,ui,v8,renderer,blink,blink_gc,mojom,"
+          "latency,latencyInfo,renderer_host,"
           "disabled-by-default-v8.gc,"
           "disabled-by-default-blink_gc,"
           "disabled-by-default-renderer.scheduler,"
diff --git a/content/public/app/BUILD.gn b/content/public/app/BUILD.gn
index a8516aa..2e831337 100644
--- a/content/public/app/BUILD.gn
+++ b/content/public/app/BUILD.gn
@@ -236,7 +236,7 @@
     "//services/resource_coordinator:manifest",
     "//services/shape_detection:manifest",
     "//services/tracing:manifest",
-    "//services/video_capture:manifest",
+    "//services/video_capture/public/cpp:manifest",
     "//services/viz:manifest",
   ]
 
diff --git a/content/public/app/DEPS b/content/public/app/DEPS
index 1f41b14..e896a38 100644
--- a/content/public/app/DEPS
+++ b/content/public/app/DEPS
@@ -4,8 +4,9 @@
   "+content/public/gpu/content_gpu_client.h",
   "+content/public/renderer/content_renderer_client.h",
   "+content/public/utility/content_utility_client.h",
-  "+services/service_manager",
   "+services/audio/public",
+  "+services/service_manager",
+  "+services/video_capture/public",
 ]
 
 specific_include_rules = {
@@ -28,7 +29,6 @@
     "+services/resource_coordinator/manifest.h",
     "+services/shape_detection/manifest.h",
     "+services/tracing/manifest.h",
-    "+services/video_capture/manifest.h",
     "+services/viz/manifest.h",
   ],
 }
diff --git a/content/public/app/content_packaged_services_manifest.cc b/content/public/app/content_packaged_services_manifest.cc
index 82b2de9..e101a5d 100644
--- a/content/public/app/content_packaged_services_manifest.cc
+++ b/content/public/app/content_packaged_services_manifest.cc
@@ -20,7 +20,7 @@
 #include "services/service_manager/public/cpp/manifest_builder.h"
 #include "services/shape_detection/manifest.h"
 #include "services/tracing/manifest.h"
-#include "services/video_capture/manifest.h"
+#include "services/video_capture/public/cpp/manifest.h"
 #include "services/viz/manifest.h"
 
 #if defined(OS_LINUX)
diff --git a/content/public/browser/tts_controller.h b/content/public/browser/tts_controller.h
index 8c03666..548ede8 100644
--- a/content/public/browser/tts_controller.h
+++ b/content/public/browser/tts_controller.h
@@ -107,10 +107,12 @@
   // Handle events received from the speech engine. Events are forwarded to
   // the callback function, and in addition, completion and error events
   // trigger finishing the current utterance and starting the next one, if
-  // any.
+  // any. If the |char_index| or |length| are not available, the speech engine
+  // should pass -1.
   virtual void OnTtsEvent(int utterance_id,
                           TtsEventType event_type,
                           int char_index,
+                          int length,
                           const std::string& error_message) = 0;
 
   // Return a list of all available voices, including the native voice,
diff --git a/content/public/browser/tts_utterance.h b/content/public/browser/tts_utterance.h
index 310cacf..534e614 100644
--- a/content/public/browser/tts_utterance.h
+++ b/content/public/browser/tts_utterance.h
@@ -45,9 +45,13 @@
 class CONTENT_EXPORT UtteranceEventDelegate {
  public:
   virtual ~UtteranceEventDelegate() {}
+  // Called when the engine reaches a TTS event in an utterance. If |char_index|
+  // or |length| are invalid or not applicable for the given |event_type|, they
+  // should be set to -1.
   virtual void OnTtsEvent(TtsUtterance* utterance,
                           TtsEventType event_type,
                           int char_index,
+                          int length,
                           const std::string& error_message) = 0;
 };
 
@@ -62,9 +66,11 @@
 
   // Sends an event to the delegate. If the event type is TTS_EVENT_END
   // or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1,
-  // uses the last good value.
+  // uses the last good value. If |length| is -1, that represents an unknown
+  // length, and will simply be passed to the delegate as -1.
   virtual void OnTtsEvent(TtsEventType event_type,
                           int char_index,
+                          int length,
                           const std::string& error_message) = 0;
 
   // Finish an utterance without sending an event to the delegate.
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index cfe87b6..2f6cf79 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -137,6 +137,7 @@
   // Note that |error_html| may be not written to in certain cases
   // (lack of information on the error code) so the caller should take care to
   // initialize it with a safe default before the call.
+  // TODO(dgozman): |ignoring_cache| is always false in these two methods.
   virtual void PrepareErrorPage(content::RenderFrame* render_frame,
                                 const blink::WebURLError& error,
                                 const std::string& http_method,
diff --git a/content/public/test/network_connection_change_simulator.cc b/content/public/test/network_connection_change_simulator.cc
index 9924ab04..14652fe 100644
--- a/content/public/test/network_connection_change_simulator.cc
+++ b/content/public/test/network_connection_change_simulator.cc
@@ -18,6 +18,15 @@
 
 namespace content {
 
+// SetConnectionType will block until the network connection changes, and
+// unblocking it involves posting a task (see
+// NetworkConnectionTracker::OnNetworkChanged). If SetConnectionType is ever
+// called downstream of a task run within another RunLoop::Run call, this
+// class's RunLoop::Run will deadlock because the task needed to unblock it
+// won't be run. To stop this, this class uses RunLoops that allow nested tasks.
+constexpr base::RunLoop::Type kRunLoopType =
+    base::RunLoop::Type::kNestableTasksAllowed;
+
 NetworkConnectionChangeSimulator::NetworkConnectionChangeSimulator() = default;
 NetworkConnectionChangeSimulator::~NetworkConnectionChangeSimulator() = default;
 
@@ -27,7 +36,7 @@
       content::GetNetworkConnectionTracker();
   network::mojom::ConnectionType connection_type =
       network::mojom::ConnectionType::CONNECTION_UNKNOWN;
-  run_loop_ = std::make_unique<base::RunLoop>();
+  run_loop_ = std::make_unique<base::RunLoop>(kRunLoopType);
   network_connection_tracker->AddNetworkConnectionObserver(this);
   SimulateNetworkChange(type);
   // Make sure the underlying network connection type becomes |type|.
@@ -56,7 +65,7 @@
     network::mojom::NetworkServiceTestPtr network_service_test;
     ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
         mojom::kNetworkServiceName, &network_service_test);
-    base::RunLoop run_loop;
+    base::RunLoop run_loop(kRunLoopType);
     network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure());
     run_loop.Run();
     return;
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 698f33f..f9bbf582 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -320,6 +320,11 @@
   layer_tree_host_->SetRootLayer(nullptr);
 }
 
+void LayerTreeView::SetNonBlinkManagedRootLayer(
+    scoped_refptr<cc::Layer> layer) {
+  layer_tree_host_->SetNonBlinkManagedRootLayer(std::move(layer));
+}
+
 cc::AnimationHost* LayerTreeView::CompositorAnimationHost() {
   return animation_host_.get();
 }
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index e41f457..a9eb7ce 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -132,6 +132,7 @@
   // blink::WebLayerTreeView implementation.
   viz::FrameSinkId GetFrameSinkId() override;
   void SetRootLayer(scoped_refptr<cc::Layer> layer) override;
+  void SetNonBlinkManagedRootLayer(scoped_refptr<cc::Layer> layer);
   void ClearRootLayer() override;
   cc::AnimationHost* CompositorAnimationHost() override;
   gfx::Size GetViewportSize() const override;
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc b/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
index 36ce8b7..15ff441 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_unittest.cc
@@ -483,9 +483,9 @@
             rtc_decoder_->Decode(input_image, false, nullptr, 0));
 }
 
-INSTANTIATE_TEST_CASE_P(CodecProfiles,
-                        RTCVideoDecoderTest,
-                        Values(webrtc::kVideoCodecVP8,
-                               webrtc::kVideoCodecH264));
+INSTANTIATE_TEST_SUITE_P(CodecProfiles,
+                         RTCVideoDecoderTest,
+                         Values(webrtc::kVideoCodecVP8,
+                                webrtc::kVideoCodecH264));
 
 }  // content
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
index c52f454..d8b087d 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
@@ -234,7 +234,7 @@
   EXPECT_FALSE(canvas_capture_handler_->NeedsNewFrame());
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     CanvasCaptureHandlerTest,
     ::testing::Combine(::testing::Bool(),
diff --git a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
index 448b609..18325df 100644
--- a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
+++ b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
@@ -194,9 +194,9 @@
   Mock::VerifyAndClearExpectations(this);
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        HTMLVideoElementCapturerSourceTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(,
+                         HTMLVideoElementCapturerSourceTest,
+                         ::testing::Bool());
 
 // When a new source is created and started, it is stopped in the same task
 // when cross-origin data is detected. This test checks that no data is
diff --git a/content/renderer/media_recorder/audio_track_recorder_unittest.cc b/content/renderer/media_recorder/audio_track_recorder_unittest.cc
index c036b160..93e253f 100644
--- a/content/renderer/media_recorder/audio_track_recorder_unittest.cc
+++ b/content/renderer/media_recorder/audio_track_recorder_unittest.cc
@@ -361,5 +361,5 @@
   Mock::VerifyAndClearExpectations(this);
 }
 
-INSTANTIATE_TEST_CASE_P(, AudioTrackRecorderTest, ValuesIn(kATRTestParams));
+INSTANTIATE_TEST_SUITE_P(, AudioTrackRecorderTest, ValuesIn(kATRTestParams));
 }  // namespace content
diff --git a/content/renderer/media_recorder/media_recorder_handler_unittest.cc b/content/renderer/media_recorder/media_recorder_handler_unittest.cc
index 8ca852b..de327bd 100644
--- a/content/renderer/media_recorder/media_recorder_handler_unittest.cc
+++ b/content/renderer/media_recorder/media_recorder_handler_unittest.cc
@@ -320,9 +320,9 @@
   media_recorder_handler_.reset();
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        MediaRecorderHandlerTest,
-                        ValuesIn(kMediaRecorderTestParams));
+INSTANTIATE_TEST_SUITE_P(,
+                         MediaRecorderHandlerTest,
+                         ValuesIn(kMediaRecorderTestParams));
 
 // Sends 2 frames and expect them as WebM (or MKV) contained encoded audio data
 // in writeData().
diff --git a/content/renderer/media_recorder/video_track_recorder_unittest.cc b/content/renderer/media_recorder/video_track_recorder_unittest.cc
index 0c4368bc..bbe1552 100644
--- a/content/renderer/media_recorder/video_track_recorder_unittest.cc
+++ b/content/renderer/media_recorder/video_track_recorder_unittest.cc
@@ -369,10 +369,10 @@
   Mock::VerifyAndClearExpectations(this);
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        VideoTrackRecorderTest,
-                        ::testing::Combine(ValuesIn(kTrackRecorderTestCodec),
-                                           ValuesIn(kTrackRecorderTestSize),
-                                           ::testing::Bool()));
+INSTANTIATE_TEST_SUITE_P(,
+                         VideoTrackRecorderTest,
+                         ::testing::Combine(ValuesIn(kTrackRecorderTestCodec),
+                                            ValuesIn(kTrackRecorderTestSize),
+                                            ::testing::Bool()));
 
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 5912b75..139c441 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -428,7 +428,6 @@
 void FillNavigationParamsRequest(
     const CommonNavigationParams& common_params,
     const CommitNavigationParams& commit_params,
-    bool is_view_source_mode_enabled,
     blink::WebNavigationParams* navigation_params) {
   // Use the original navigation url to start with. We'll replay the redirects
   // afterwards and will eventually arrive to the final url.
@@ -439,9 +438,6 @@
       !commit_params.original_method.empty() ? commit_params.original_method
                                              : common_params.method);
 
-  if (is_view_source_mode_enabled)
-    navigation_params->cache_mode = blink::mojom::FetchCacheMode::kForceCache;
-
   if (common_params.referrer.url.is_valid()) {
     WebString referrer = WebSecurityPolicy::GenerateReferrerHeader(
         common_params.referrer.policy, common_params.url,
@@ -2821,9 +2817,7 @@
   } else {
     GetContentClient()->renderer()->PrepareErrorPage(
         this, error, document_loader->HttpMethod().Ascii(),
-        document_loader->GetCacheMode() ==
-            blink::mojom::FetchCacheMode::kBypassCache,
-        &error_html);
+        false /* ignoring_cache */, &error_html);
   }
 
   // Make sure we never show errors in view source mode.
@@ -3279,7 +3273,6 @@
     return;
   }
 
-  base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
   // Check if the navigation being committed originated as a client redirect.
   bool is_client_redirect =
       !!(common_params.transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT);
@@ -3316,7 +3309,6 @@
     InternalDocumentStateData* internal_data =
         InternalDocumentStateData::FromDocumentState(document_state.get());
     FillNavigationParamsRequest(common_params, commit_params,
-                                frame_->IsViewSourceModeEnabled(),
                                 navigation_params.get());
     NavigationBodyLoader::FillNavigationParamsResponseAndBodyLoader(
         common_params, commit_params, internal_data->request_id(), head,
@@ -3325,18 +3317,10 @@
         !frame_->Parent(), navigation_params.get());
   }
 
-  // Note: we cannot use base::AutoReset here, since |this| can be deleted
-  // in the next call and auto reset will introduce use-after-free bug.
-  committing_main_request_ = true;
   frame_->CommitNavigation(std::move(navigation_params),
                            std::move(document_state));
-  // The commit can result in this frame being removed. Use a
-  // WeakPtr as an easy way to detect whether this has occured. If so, this
-  // method should return immediately and not touch any part of the object,
-  // otherwise it will result in a use-after-free bug.
-  if (!weak_this)
-    return;
-  committing_main_request_ = false;
+  // The commit can result in this frame being removed. Do not use
+  // |this| without checking a WeakPtr.
 }
 
 void RenderFrameImpl::CommitFailedNavigation(
@@ -3381,10 +3365,8 @@
 
   auto navigation_params = std::make_unique<WebNavigationParams>();
   FillNavigationParamsRequest(common_params, commit_params,
-                              frame_->IsViewSourceModeEnabled(),
                               navigation_params.get());
   navigation_params->url = GURL(kUnreachableWebDataURL);
-  navigation_params->cache_mode = blink::mojom::FetchCacheMode::kNoStore;
 
   if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params.url)) {
     // The browser expects this frame to be loading an error page. Inform it
@@ -4581,9 +4563,7 @@
     std::string error_html;
     GetContentClient()->renderer()->PrepareErrorPageForHttpStatusError(
         this, unreachable_url, document_loader->HttpMethod().Ascii(),
-        document_loader->GetCacheMode() ==
-            blink::mojom::FetchCacheMode::kBypassCache,
-        http_status_code, &error_html);
+        false /* ignoring_cache */, http_status_code, &error_html);
     // This call may run scripts, e.g. via the beforeunload event, and possibly
     // delete |this|.
     LoadNavigationErrorPage(document_loader,
@@ -4866,14 +4846,6 @@
 }
 
 void RenderFrameImpl::WillSendRequest(blink::WebURLRequest& request) {
-  if (committing_main_request_ &&
-      request.GetFrameType() !=
-          network::mojom::RequestContextFrameType::kNone) {
-    // We should not process this request, as it was already processed
-    // as part of BeginNavigation.
-    return;
-  }
-
   if (render_view_->renderer_preferences_.enable_do_not_track)
     request.SetHTTPHeaderField(blink::WebString::FromUTF8(kDoNotTrackHeader),
                                "1");
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 16292b9..1ef0b77a 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1634,13 +1634,6 @@
   int num_burst_download_requests_ = 0;
   base::TimeTicks burst_download_start_time_;
 
-  // Set to true while we are committing a navigation and
-  // main request is being issued (the one which already got
-  // a response).
-  // TODO(dgozman): should be temporary until we stop using
-  // WebURLRequest for this.
-  bool committing_main_request_ = false;
-
   RenderFrameMediaPlaybackOptions renderer_media_playback_options_;
 
   base::CancelableOnceCallback<void()> sync_navigation_callback_;
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 8039d5e..9774ed67 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -355,7 +355,7 @@
   }
   UpdateLayerBounds();
   layer_->SetIsDrawable(true);
-  layer_tree_view()->SetRootLayer(layer_);
+  layer_tree_view()->SetNonBlinkManagedRootLayer(layer_);
 }
 
 bool RenderWidgetFullscreenPepper::OnMessageReceived(const IPC::Message& msg) {
diff --git a/content/test/gpu/gpu_tests/info_collection_test.py b/content/test/gpu/gpu_tests/info_collection_test.py
index f98b665..7f39ed3 100644
--- a/content/test/gpu/gpu_tests/info_collection_test.py
+++ b/content/test/gpu/gpu_tests/info_collection_test.py
@@ -7,6 +7,14 @@
 
 import sys
 
+# Please expand the following lists when we expand to new bot configs.
+_SUPPORTED_WIN_VERSIONS = ['win7', 'win10']
+_SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION = ['win10']
+_SUPPORTED_WIN_GPU_VENDORS = [0x8086, 0x10de, 0x1002]
+_SUPPORTED_WIN_INTEL_GPUS = [0x5912]
+_SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS = [0x5912]
+_SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS = [0x5912]
+
 # There are no expectations for info_collection
 class InfoCollectionExpectations(GpuTestExpectations):
   def SetExpectations(self):
@@ -35,6 +43,32 @@
     cls.CustomizeBrowserArgs([])
     cls.StartBrowser()
 
+  def _GetOverlayExpectations(self, os_version, gpu_vendor_id, gpu_device_id):
+    # The rules to set up per bot expectations are:
+    #  1) Only win10 or newer supports DirectComposition
+    #  2) Only Intel supports hardware overlays with DirectComposition
+    #  3) Currently the Win/Intel GPU bot supports YUY2 and NV12 overlays
+    expectations = {
+      'direct_composition': False,
+      'supports_overlays': False,
+      'overlay_cap_yuy2': 'NONE',
+      'overlay_cap_nv12': 'NONE',
+    }
+    assert os_version is not None
+    os_version = os_version.lower()
+    assert os_version in _SUPPORTED_WIN_VERSIONS
+    assert gpu_vendor_id in _SUPPORTED_WIN_GPU_VENDORS
+    if os_version in _SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION:
+      expectations['direct_composition'] = True
+      if gpu_vendor_id == 0x8086:
+        expectations['supports_overlays'] = True
+        assert gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS
+        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS:
+          expectations['overlay_cap_yuy2'] = 'SCALING'
+        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS:
+          expectations['overlay_cap_nv12'] = 'SCALING'
+    return expectations
+
   def RunActualGpuTest(self, test_path, *args):
     # Make sure the GPU process is started
     self.tab.action_runner.Navigate('chrome:gpu')
@@ -52,21 +86,44 @@
     detected_device_id = gpu.device_id
 
     # Gather the expected IDs passed on the command line
-    if args[0] == None or args[1] == None:
+    if args[0] is None or args[1] is None:
       self.fail("Missing --expected-[vendor|device]-id command line args")
 
     expected_vendor_id = int(args[0], 16)
     expected_device_id = int(args[1], 16)
 
-    # Check expected and detected matches
+    # Check expected and detected GPUs match
     if detected_vendor_id != expected_vendor_id:
       self.fail('Vendor ID mismatch, expected %s but got %s.' %
           (expected_vendor_id, detected_vendor_id))
 
     if detected_device_id != expected_device_id:
-      self.fail('device ID mismatch, expected %s but got %s.' %
+      self.fail('Device ID mismatch, expected %s but got %s.' %
           (expected_device_id, detected_device_id))
 
+    os_name = self.browser.platform.GetOSName()
+    if os_name and os_name.lower() == 'win':
+      expectations = self._GetOverlayExpectations(
+          self.browser.platform.GetOSVersionName(),
+          detected_vendor_id, detected_device_id)
+
+      aux_attributes = system_info.gpu.aux_attributes
+      if not aux_attributes:
+        self.fail('GPU info does not have aux_attributes.')
+
+      for (field, expected) in expectations.iteritems():
+        detected = aux_attributes.get(field, 'NONE')
+        if  expected != detected:
+          self.fail('%s mismatch, expected %s but got %s.' %
+              (field, self._ValueToStr(expected), self._ValueToStr(detected)))
+
+  def _ValueToStr(self, value):
+    if type(value) is str:
+      return value
+    if type(value) is bool:
+      return 'supported' if value else 'unsupported'
+    assert False
+
   @classmethod
   def _CreateExpectations(cls):
     return InfoCollectionExpectations()
diff --git a/gpu/command_buffer/service/gl_stream_texture_image_stub.cc b/gpu/command_buffer/service/gl_stream_texture_image_stub.cc
index ad14c9db..4abec36 100644
--- a/gpu/command_buffer/service/gl_stream_texture_image_stub.cc
+++ b/gpu/command_buffer/service/gl_stream_texture_image_stub.cc
@@ -16,10 +16,15 @@
 unsigned GLStreamTextureImageStub::GetInternalFormat() {
   return 0;
 }
+GLStreamTextureImageStub::BindOrCopy
+GLStreamTextureImageStub::ShouldBindOrCopy() {
+  return BIND;
+}
 bool GLStreamTextureImageStub::BindTexImage(unsigned target) {
   return false;
 }
 bool GLStreamTextureImageStub::CopyTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 bool GLStreamTextureImageStub::CopyTexSubImage(unsigned target,
diff --git a/gpu/command_buffer/service/gl_stream_texture_image_stub.h b/gpu/command_buffer/service/gl_stream_texture_image_stub.h
index bf55d29..8de116f9 100644
--- a/gpu/command_buffer/service/gl_stream_texture_image_stub.h
+++ b/gpu/command_buffer/service/gl_stream_texture_image_stub.h
@@ -17,6 +17,7 @@
   // Overridden from GLImage:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override {}
   bool CopyTexImage(unsigned target) override;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 82a959d8..d40f364 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -10340,7 +10340,9 @@
       if (texture_unit)
         api()->glActiveTextureFn(texture_unit);
       api()->glBindTextureFn(textarget, texture->service_id());
-      if (image->BindTexImage(textarget)) {
+      if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+        bool rv = image->BindTexImage(textarget);
+        DCHECK(rv) << "BindTexImage() failed";
         image_state = Texture::BOUND;
       } else {
         DoCopyTexImage(texture, textarget, image);
@@ -17443,8 +17445,10 @@
   if (image && internal_format == source_internal_format && dest_level == 0 &&
       !unpack_flip_y && !unpack_premultiply_alpha_change) {
     api()->glBindTextureFn(dest_binding_target, dest_texture->service_id());
-    if (image->CopyTexImage(dest_target))
+    if (image->ShouldBindOrCopy() == gl::GLImage::COPY &&
+        image->CopyTexImage(dest_target)) {
       return;
+    }
   }
 
   DoBindOrCopyTexImageIfNeeded(source_texture, source_target, 0);
@@ -18334,7 +18338,7 @@
 
   Texture::ImageState image_state = Texture::UNBOUND;
 
-  {
+  if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
     ScopedGLErrorSuppressor suppressor(
         "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM", error_state_.get());
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 5800313..805d9ae 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1686,7 +1686,9 @@
     return;
 
   // TODO: internalformat?
-  if (!image->BindTexImage(target))
+  if (image->ShouldBindOrCopy() == gl::GLImage::BIND)
+    image->BindTexImage(target);
+  else
     image->CopyTexImage(target);
 
   // If copy / bind fail, then we could keep the bind state the same.
@@ -2437,14 +2439,13 @@
     return error::kNoError;
   }
 
-  if (internalformat) {
-    if (!image->BindTexImageWithInternalformat(target, internalformat)) {
-      image->CopyTexImage(target);
-    }
+  if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+    if (internalformat)
+      image->BindTexImageWithInternalformat(target, internalformat);
+    else
+      image->BindTexImage(target);
   } else {
-    if (!image->BindTexImage(target)) {
-      image->CopyTexImage(target);
-    }
+    image->CopyTexImage(target);
   }
 
   // Target is already validated
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 856224e9..ad7830a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -1407,6 +1407,7 @@
   cmds::BindTexImage2DCHROMIUM bind_tex_image_2d_cmd;
   bind_tex_image_2d_cmd.Init(target, image_id);
   EXPECT_CALL(*gl_, GetError())
+      .Times(AtMost(2))
       .WillOnce(Return(GL_NO_ERROR))
       .WillOnce(Return(GL_NO_ERROR))
       .RetiresOnSaturation();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index b94cd64..a858e10 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -3766,6 +3766,7 @@
   // Overridden from gl::GLImage:
   MOCK_METHOD0(GetSize, gfx::Size());
   MOCK_METHOD0(GetInternalFormat, unsigned());
+  MOCK_METHOD0(ShouldBindOrCopy, gl::GLImage::BindOrCopy());
   MOCK_METHOD1(BindTexImage, bool(unsigned));
   MOCK_METHOD1(ReleaseTexImage, void(unsigned));
   MOCK_METHOD1(CopyTexImage, bool(unsigned));
@@ -3806,9 +3807,10 @@
   GetImageManagerForTest()->AddImage(image.get(), kImageId);
 
   // Bind image to texture.
+  EXPECT_CALL(*image.get(), ShouldBindOrCopy())
+      .WillRepeatedly(Return(gl::GLImage::COPY));
   EXPECT_CALL(*image.get(), BindTexImage(GL_TEXTURE_2D))
-      .Times(1)
-      .WillRepeatedly(Return(false))
+      .Times(0)
       .RetiresOnSaturation();
   EXPECT_CALL(*image.get(), GetSize())
       .Times(1)
@@ -3845,8 +3847,7 @@
         .Times(1)
         .RetiresOnSaturation();
     EXPECT_CALL(*image.get(), BindTexImage(GL_TEXTURE_2D))
-        .Times(1)
-        .WillRepeatedly(Return(false))
+        .Times(0)
         .RetiresOnSaturation();
     EXPECT_CALL(*image.get(), CopyTexImage(GL_TEXTURE_2D))
         .Times(1)
@@ -3873,8 +3874,7 @@
   release_tex_image_2d_cmd.Init(GL_TEXTURE_2D, kImageId);
   EXPECT_EQ(error::kNoError, ExecuteCmd(release_tex_image_2d_cmd));
   EXPECT_CALL(*image.get(), BindTexImage(GL_TEXTURE_2D))
-      .Times(2)
-      .WillRepeatedly(Return(false))
+      .Times(0)
       .RetiresOnSaturation();
   EXPECT_CALL(*image.get(), GetSize())
       .Times(1)
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index c000932..3ae1bb8 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -1690,9 +1690,10 @@
       if (image) {
         api()->glBindTextureFn(source_texture->target(),
                                source_texture->service_id());
-        if (!image->BindTexImage(source_texture->target())) {
+        if (image->ShouldBindOrCopy() == gl::GLImage::BIND)
+          image->BindTexImage(source_texture->target());
+        else
           image->CopyTexImage(source_texture->target());
-        }
         source_texture->set_is_bind_pending(false);
       }
     }
@@ -1961,7 +1962,10 @@
           "RasterDecoderImpl::DoBindOrCopyTexImageIfNeeded",
           error_state_.get());
       api()->glBindTextureFn(textarget, texture->service_id());
-      if (!image->BindTexImage(textarget)) {
+      if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+        bool rv = image->BindTexImage(textarget);
+        DCHECK(rv) << "BindTexImage() failed";
+      } else {
         // Note: We update the state to COPIED prior to calling CopyTexImage()
         // as that allows the GLImage implemenatation to set it back to
         // UNBOUND and ensure that CopyTexImage() is called each time the
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 7154228..b60c7fbc 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -348,8 +348,10 @@
       return;
     image->ReleaseTexImage(target);
     gles2::Texture::ImageState new_state = gles2::Texture::UNBOUND;
-    if (image->BindTexImage(target))
+    if (image->ShouldBindOrCopy() == gl::GLImage::BIND &&
+        image->BindTexImage(target)) {
       new_state = gles2::Texture::BOUND;
+    }
     if (old_state != new_state)
       texture_->SetLevelImage(target, 0, image, new_state);
   }
@@ -449,7 +451,9 @@
     if (!image)
       return;
     image->ReleaseTexImage(target);
-    if (!image->BindTexImage(target))
+    if (image->ShouldBindOrCopy() == gl::GLImage::BIND)
+      image->BindTexImage(target);
+    else
       image->CopyTexImage(target);
   }
 
@@ -718,6 +722,8 @@
     image = image_factory_->CreateAnonymousImage(
         size, format_info.buffer_format, gfx::BufferUsage::SCANOUT,
         &is_cleared);
+    // A SCANOUT image should not require copy.
+    DCHECK(!image || image->ShouldBindOrCopy() == gl::GLImage::BIND);
     if (!image || !image->BindTexImage(target)) {
       LOG(ERROR) << "CreateSharedImage: Failed to create image";
       api->glDeleteTexturesFn(1, &service_id);
@@ -811,7 +817,12 @@
 
   // TODO(piman): RGB emulation
   gles2::Texture::ImageState image_state = gles2::Texture::UNBOUND;
-  if (image->BindTexImage(target)) {
+  if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+    if (!image->BindTexImage(target)) {
+      LOG(ERROR) << "Failed to bind image to target.";
+      api->glDeleteTexturesFn(1, &service_id);
+      return nullptr;
+    }
     image_state = gles2::Texture::BOUND;
   } else if (use_passthrough_) {
     image->CopyTexImage(target);
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index a1ee348..dabbe757 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -526,6 +526,8 @@
     return InternalFormatForGpuMemoryBufferFormat(format_);
   }
 
+  BindOrCopy ShouldBindOrCopy() override { return BIND; }
+
   bool BindTexImage(unsigned target) override {
     if (!bound_) {
       bound_ = true;
diff --git a/gpu/command_buffer/service/texture_definition.cc b/gpu/command_buffer/service/texture_definition.cc
index 8d6e07c..91288c6f 100644
--- a/gpu/command_buffer/service/texture_definition.cc
+++ b/gpu/command_buffer/service/texture_definition.cc
@@ -34,6 +34,7 @@
   // Implement GLImage.
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
@@ -83,6 +84,10 @@
   return GL_RGBA;
 }
 
+GLImageSync::BindOrCopy GLImageSync::ShouldBindOrCopy() {
+  return BIND;
+}
+
 bool GLImageSync::BindTexImage(unsigned target) {
   NOTREACHED();
   return false;
diff --git a/gpu/command_buffer/tests/texture_image_factory.cc b/gpu/command_buffer/tests/texture_image_factory.cc
index f37ed61..5356230 100644
--- a/gpu/command_buffer/tests/texture_image_factory.cc
+++ b/gpu/command_buffer/tests/texture_image_factory.cc
@@ -16,6 +16,7 @@
 
   gfx::Size GetSize() override { return size_; }
   unsigned GetInternalFormat() override { return GL_RGBA; }
+  BindOrCopy ShouldBindOrCopy() override { return BIND; }
   bool BindTexImage(unsigned target) override {
     glTexImage2D(target,
                  0,  // mip level
@@ -25,7 +26,10 @@
     return true;
   }
   void ReleaseTexImage(unsigned target) override {}
-  bool CopyTexImage(unsigned target) override { return false; }
+  bool CopyTexImage(unsigned target) override {
+    NOTREACHED();
+    return false;
+  }
   bool CopyTexSubImage(unsigned target,
                        const gfx::Point& offset,
                        const gfx::Rect& rect) override {
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 9525f669..3d34489 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -53,9 +53,11 @@
 #if defined(OS_WIN)
 void EnumerateOverlayCapability(const gpu::OverlayCapability& cap,
                                 gpu::GPUInfo::Enumerator* enumerator) {
+  std::string key_string = "overlayCap";
+  key_string += OverlayFormatToString(cap.format);
   enumerator->BeginOverlayCapability();
-  enumerator->AddInt("format", static_cast<int>(cap.format));
-  enumerator->AddInt("isScalingSupported", cap.is_scaling_supported);
+  enumerator->AddString(key_string.c_str(),
+                        cap.is_scaling_supported ? "SCALING" : "DIRECT");
   enumerator->EndOverlayCapability();
 }
 
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index a1dc375..4bbcaa9 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -219,7 +219,12 @@
                                              surface_owner_.get());
 }
 
+StreamTexture::BindOrCopy StreamTexture::ShouldBindOrCopy() {
+  return COPY;
+}
+
 bool StreamTexture::BindTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index a284ae66..0fbac37 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -46,6 +46,7 @@
   // gl::GLImage implementation:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
diff --git a/gpu/vulkan/android/vulkan_implementation_android.cc b/gpu/vulkan/android/vulkan_implementation_android.cc
index 275fb419..5872267 100644
--- a/gpu/vulkan/android/vulkan_implementation_android.cc
+++ b/gpu/vulkan/android/vulkan_implementation_android.cc
@@ -48,8 +48,8 @@
   return true;
 }
 
-VkInstance VulkanImplementationAndroid::GetVulkanInstance() {
-  return vulkan_instance_.vk_instance();
+VulkanInstance* VulkanImplementationAndroid::GetVulkanInstance() {
+  return &vulkan_instance_;
 }
 
 std::unique_ptr<VulkanSurface> VulkanImplementationAndroid::CreateViewSurface(
@@ -59,13 +59,14 @@
   surface_create_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
   surface_create_info.window = window;
   VkResult result = vkCreateAndroidSurfaceKHR_(
-      GetVulkanInstance(), &surface_create_info, nullptr, &surface);
+      vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
   if (VK_SUCCESS != result) {
     DLOG(ERROR) << "vkCreateAndroidSurfaceKHR() failed: " << result;
     return nullptr;
   }
 
-  return std::make_unique<VulkanSurface>(GetVulkanInstance(), surface);
+  return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(),
+                                         surface);
 }
 
 bool VulkanImplementationAndroid::GetPhysicalDevicePresentationSupport(
diff --git a/gpu/vulkan/android/vulkan_implementation_android.h b/gpu/vulkan/android/vulkan_implementation_android.h
index 145320d..ff66a77 100644
--- a/gpu/vulkan/android/vulkan_implementation_android.h
+++ b/gpu/vulkan/android/vulkan_implementation_android.h
@@ -23,7 +23,7 @@
 
   // VulkanImplementation:
   bool InitializeVulkanInstance() override;
-  VkInstance GetVulkanInstance() override;
+  VulkanInstance* GetVulkanInstance() override;
   std::unique_ptr<VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) override;
   bool GetPhysicalDevicePresentationSupport(
diff --git a/gpu/vulkan/vulkan_function_pointers.cc b/gpu/vulkan/vulkan_function_pointers.cc
index ef19bfb..3f2e82a 100644
--- a/gpu/vulkan/vulkan_function_pointers.cc
+++ b/gpu/vulkan/vulkan_function_pointers.cc
@@ -37,6 +37,12 @@
   if (!vkCreateInstanceFn)
     return false;
 
+  vkEnumerateInstanceVersionFn =
+      reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
+          vkGetInstanceProcAddrFn(nullptr, "vkEnumerateInstanceVersion"));
+  // vkEnumerateInstanceVersion didn't exist in Vulkan 1.0, so we should
+  // proceed even if we fail to get vkEnumerateInstanceVersion pointer.
+
   vkEnumerateInstanceExtensionPropertiesFn =
       reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
           vkGetInstanceProcAddrFn(nullptr,
diff --git a/gpu/vulkan/vulkan_function_pointers.h b/gpu/vulkan/vulkan_function_pointers.h
index abc4a37..7dd3d2f 100644
--- a/gpu/vulkan/vulkan_function_pointers.h
+++ b/gpu/vulkan/vulkan_function_pointers.h
@@ -42,6 +42,7 @@
   // Unassociated functions
   PFN_vkGetInstanceProcAddr vkGetInstanceProcAddrFn = nullptr;
   PFN_vkCreateInstance vkCreateInstanceFn = nullptr;
+  PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersionFn = nullptr;
   PFN_vkEnumerateInstanceExtensionProperties
       vkEnumerateInstanceExtensionPropertiesFn = nullptr;
   PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerPropertiesFn =
diff --git a/gpu/vulkan/vulkan_implementation.cc b/gpu/vulkan/vulkan_implementation.cc
index c0a54c9..b4655d1 100644
--- a/gpu/vulkan/vulkan_implementation.cc
+++ b/gpu/vulkan/vulkan_implementation.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_instance.h"
 
 namespace gpu {
 
@@ -17,7 +18,7 @@
     VulkanImplementation* vulkan_implementation,
     uint32_t option) {
   auto device_queue = std::make_unique<VulkanDeviceQueue>(
-      vulkan_implementation->GetVulkanInstance());
+      vulkan_implementation->GetVulkanInstance()->vk_instance());
   auto callback = base::BindRepeating(
       &VulkanImplementation::GetPhysicalDevicePresentationSupport,
       base::Unretained(vulkan_implementation));
diff --git a/gpu/vulkan/vulkan_implementation.h b/gpu/vulkan/vulkan_implementation.h
index 78470bc..721db21 100644
--- a/gpu/vulkan/vulkan_implementation.h
+++ b/gpu/vulkan/vulkan_implementation.h
@@ -28,6 +28,7 @@
 
 class VulkanDeviceQueue;
 class VulkanSurface;
+class VulkanInstance;
 
 // This object provides factory functions for creating vulkan objects that use
 // platform-specific extensions (e.g. for creation of VkSurfaceKHR objects).
@@ -39,7 +40,7 @@
 
   virtual bool InitializeVulkanInstance() = 0;
 
-  virtual VkInstance GetVulkanInstance() = 0;
+  virtual VulkanInstance* GetVulkanInstance() = 0;
 
   virtual std::unique_ptr<VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) = 0;
diff --git a/gpu/vulkan/vulkan_instance.cc b/gpu/vulkan/vulkan_instance.cc
index c733e656..5020258 100644
--- a/gpu/vulkan/vulkan_instance.cc
+++ b/gpu/vulkan/vulkan_instance.cc
@@ -56,12 +56,23 @@
   if (!vulkan_function_pointers->BindUnassociatedFunctionPointers())
     return false;
 
+  uint32_t supported_api_version = VK_MAKE_VERSION(1, 0, 0);
+  if (vulkan_function_pointers->vkEnumerateInstanceVersionFn) {
+    vulkan_function_pointers->vkEnumerateInstanceVersionFn(
+        &supported_api_version);
+  }
+
+  // Use Vulkan 1.1 if it's available.
+  api_version_ = (supported_api_version >= VK_MAKE_VERSION(1, 1, 0))
+                     ? VK_MAKE_VERSION(1, 1, 0)
+                     : VK_MAKE_VERSION(1, 0, 0);
+
   VkResult result = VK_SUCCESS;
 
   VkApplicationInfo app_info = {};
   app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
   app_info.pApplicationName = "Chromium";
-  app_info.apiVersion = VK_MAKE_VERSION(1, 1, 0);
+  app_info.apiVersion = api_version_;
 
   std::vector<const char*> enabled_extensions;
   enabled_extensions.insert(std::end(enabled_extensions),
diff --git a/gpu/vulkan/vulkan_instance.h b/gpu/vulkan/vulkan_instance.h
index 19ba467..2762d97 100644
--- a/gpu/vulkan/vulkan_instance.h
+++ b/gpu/vulkan/vulkan_instance.h
@@ -29,6 +29,9 @@
   bool Initialize(const std::vector<const char*>& required_extensions,
                   const std::vector<const char*>& required_layers);
 
+  // VkApplicationInfo.apiVersion value used to initialize the instance.
+  uint32_t api_version() const { return api_version_; }
+
   const gfx::ExtensionSet& enabled_extensions() const {
     return enabled_extensions_;
   }
@@ -38,6 +41,8 @@
  private:
   void Destroy();
 
+  uint32_t api_version_;
+
   VkInstance vk_instance_ = VK_NULL_HANDLE;
   gfx::ExtensionSet enabled_extensions_;
   bool debug_report_enabled_ = false;
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.cc b/gpu/vulkan/win32/vulkan_implementation_win32.cc
index 2530aafd..f0a6bec 100644
--- a/gpu/vulkan/win32/vulkan_implementation_win32.cc
+++ b/gpu/vulkan/win32/vulkan_implementation_win32.cc
@@ -55,8 +55,8 @@
   return true;
 }
 
-VkInstance VulkanImplementationWin32::GetVulkanInstance() {
-  return vulkan_instance_.vk_instance();
+VulkanInstance* VulkanImplementationWin32::GetVulkanInstance() {
+  return &vulkan_instance_;
 }
 
 std::unique_ptr<VulkanSurface> VulkanImplementationWin32::CreateViewSurface(
@@ -68,13 +68,14 @@
       reinterpret_cast<HINSTANCE>(GetWindowLongPtr(window, GWLP_HINSTANCE));
   surface_create_info.hwnd = window;
   VkResult result = vkCreateWin32SurfaceKHR_(
-      GetVulkanInstance(), &surface_create_info, nullptr, &surface);
+      vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
   if (VK_SUCCESS != result) {
     DLOG(ERROR) << "vkCreatWin32SurfaceKHR() failed: " << result;
     return nullptr;
   }
 
-  return std::make_unique<VulkanSurface>(GetVulkanInstance(), surface);
+  return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(),
+                                         surface);
 }
 
 bool VulkanImplementationWin32::GetPhysicalDevicePresentationSupport(
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.h b/gpu/vulkan/win32/vulkan_implementation_win32.h
index 43c3352..dde9960 100644
--- a/gpu/vulkan/win32/vulkan_implementation_win32.h
+++ b/gpu/vulkan/win32/vulkan_implementation_win32.h
@@ -21,7 +21,7 @@
 
   // VulkanImplementation:
   bool InitializeVulkanInstance() override;
-  VkInstance GetVulkanInstance() override;
+  VulkanInstance* GetVulkanInstance() override;
   std::unique_ptr<VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) override;
   bool GetPhysicalDevicePresentationSupport(
diff --git a/gpu/vulkan/x/vulkan_implementation_x11.cc b/gpu/vulkan/x/vulkan_implementation_x11.cc
index 1f390e7..e0dd99a 100644
--- a/gpu/vulkan/x/vulkan_implementation_x11.cc
+++ b/gpu/vulkan/x/vulkan_implementation_x11.cc
@@ -61,8 +61,8 @@
   return true;
 }
 
-VkInstance VulkanImplementationX11::GetVulkanInstance() {
-  return vulkan_instance_.vk_instance();
+VulkanInstance* VulkanImplementationX11::GetVulkanInstance() {
+  return &vulkan_instance_;
 }
 
 std::unique_ptr<VulkanSurface> VulkanImplementationX11::CreateViewSurface(
@@ -73,13 +73,14 @@
   surface_create_info.dpy = x_display_;
   surface_create_info.window = window;
   VkResult result = vkCreateXlibSurfaceKHR_(
-      GetVulkanInstance(), &surface_create_info, nullptr, &surface);
+      vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
   if (VK_SUCCESS != result) {
     DLOG(ERROR) << "vkCreateXlibSurfaceKHR() failed: " << result;
     return nullptr;
   }
 
-  return std::make_unique<VulkanSurface>(GetVulkanInstance(), surface);
+  return std::make_unique<VulkanSurface>(vulkan_instance_.vk_instance(),
+                                         surface);
 }
 
 bool VulkanImplementationX11::GetPhysicalDevicePresentationSupport(
diff --git a/gpu/vulkan/x/vulkan_implementation_x11.h b/gpu/vulkan/x/vulkan_implementation_x11.h
index 49ed244..d6e6ff7f 100644
--- a/gpu/vulkan/x/vulkan_implementation_x11.h
+++ b/gpu/vulkan/x/vulkan_implementation_x11.h
@@ -23,7 +23,7 @@
 
   // VulkanImplementation:
   bool InitializeVulkanInstance() override;
-  VkInstance GetVulkanInstance() override;
+  VulkanInstance* GetVulkanInstance() override;
   std::unique_ptr<VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) override;
   bool GetPhysicalDevicePresentationSupport(
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
index 5feec3bb..55c28917 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.cc
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 
 #include "base/bind.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -32,7 +32,8 @@
 // static
 IOSChromeContentSuggestionsServiceFactory*
 IOSChromeContentSuggestionsServiceFactory::GetInstance() {
-  return base::Singleton<IOSChromeContentSuggestionsServiceFactory>::get();
+  static base::NoDestructor<IOSChromeContentSuggestionsServiceFactory> instance;
+  return instance.get();
 }
 
 // static
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h
index 3f7903a..23d038b4 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h
@@ -8,13 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/no_destructor.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace ios {
 class ChromeBrowserState;
 }  // namespace ios
@@ -38,8 +34,7 @@
   static TestingFactory GetDefaultFactory();
 
  private:
-  friend struct base::DefaultSingletonTraits<
-      IOSChromeContentSuggestionsServiceFactory>;
+  friend class base::NoDestructor<IOSChromeContentSuggestionsServiceFactory>;
 
   IOSChromeContentSuggestionsServiceFactory();
   ~IOSChromeContentSuggestionsServiceFactory() override;
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
index 55e3b6c..7dfb2645 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -11,7 +11,6 @@
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/json/json_reader.h"
-#include "base/memory/singleton.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 4a7755a..39b32d5f 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -4685,22 +4685,6 @@
   // Reset horizontal stack view.
   [sideSwipeView removeFromSuperview];
   [self.sideSwipeController setInSwipe:NO];
-
-  if (base::FeatureList::IsEnabled(kPresentSadTabInViewController)) {
-    web::WebState* webState = self.currentWebState;
-    if (webState && webState->IsVisible()) {
-      // Side swipe did not change the tab represented by |webState|.
-      SadTabTabHelper* sadTabHelper = SadTabTabHelper::FromWebState(webState);
-      if (sadTabHelper && sadTabHelper->is_showing_sad_tab()) {
-        SadTabCoordinator* sadTabCoordinator =
-            base::mac::ObjCCastStrict<SadTabCoordinator>(_sadTabCoordinator);
-        [sadTabCoordinator start];
-      }
-    } else {
-      // Side swipe did change the tab and |webState| represents previous tab
-      // which is not visible anymore.
-    }
-  }
 }
 
 - (UIView*)sideSwipeContentView {
@@ -4770,14 +4754,6 @@
   return self.primaryToolbarCoordinator.viewController.view;
 }
 
-- (void)didRemoveSideSwipeContentView {
-  if (base::FeatureList::IsEnabled(kPresentSadTabInViewController)) {
-    SadTabCoordinator* sadTabCoordinator =
-        base::mac::ObjCCastStrict<SadTabCoordinator>(_sadTabCoordinator);
-    [sadTabCoordinator stop];
-  }
-}
-
 #pragma mark - PreloadControllerDelegate methods
 
 - (BOOL)preloadShouldUseDesktopUserAgent {
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
index 0fdcd2fe..abf17dd 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
@@ -46,8 +46,6 @@
 - (BOOL)canBeginToolbarSwipe;
 // Returns the top toolbar's view.
 - (UIView*)topToolbarView;
-// Called after removing |sideSwipeContentView|.
-- (void)didRemoveSideSwipeContentView;
 
 @end
 
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 5f2c10f..fbd77976da 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -549,7 +549,6 @@
 
     // Remove content area so it doesn't receive any pan events.
     [[swipeDelegate_ sideSwipeContentView] removeFromSuperview];
-    [swipeDelegate_ didRemoveSideSwipeContentView];
   }
 
   [tabSideSwipeView_ handleHorizontalPan:gesture];
diff --git a/ios/chrome/browser/ui/webui/flags_ui.cc b/ios/chrome/browser/ui/webui/flags_ui.cc
index c48b6e3..ea8e84a 100644
--- a/ios/chrome/browser/ui/webui/flags_ui.cc
+++ b/ios/chrome/browser/ui/webui/flags_ui.cc
@@ -146,8 +146,10 @@
 
   results.SetBoolean(flags_ui::kShowBetaChannelPromotion, false);
   results.SetBoolean(flags_ui::kShowDevChannelPromotion, false);
+
+  std::vector<const base::Value*> params{&results};
   web_ui()->CallJavascriptFunction(flags_ui::kReturnExperimentalFeatures,
-                                   results);
+                                   params);
 }
 
 void FlagsDOMHandler::HandleEnableExperimentalFeatureMessage(
diff --git a/ios/chrome/browser/ui/webui/omaha_ui.cc b/ios/chrome/browser/ui/webui/omaha_ui.cc
index a21fdc9..78db200 100644
--- a/ios/chrome/browser/ui/webui/omaha_ui.cc
+++ b/ios/chrome/browser/ui/webui/omaha_ui.cc
@@ -77,8 +77,8 @@
 
 void OmahaDOMHandler::OnDebugInformationAvailable(
     base::DictionaryValue* debug_information) {
-  web_ui()->CallJavascriptFunction("updateOmahaDebugInformation",
-                                   *debug_information);
+  std::vector<const base::Value*> args{debug_information};
+  web_ui()->CallJavascriptFunction("updateOmahaDebugInformation", args);
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/ui/webui/password_manager_internals_ui_ios.mm b/ios/chrome/browser/ui/webui/password_manager_internals_ui_ios.mm
index ab9e25f..f37322c 100644
--- a/ios/chrome/browser/ui/webui/password_manager_internals_ui_ios.mm
+++ b/ios/chrome/browser/ui/webui/password_manager_internals_ui_ios.mm
@@ -70,8 +70,9 @@
   std::string no_quotes(text);
   std::replace(no_quotes.begin(), no_quotes.end(), '"', ' ');
   base::Value text_string_value(net::EscapeForHTML(no_quotes));
-  web_ui()->CallJavascriptFunction("addSavePasswordProgressLog",
-                                   text_string_value);
+
+  std::vector<const base::Value*> args{&text_string_value};
+  web_ui()->CallJavascriptFunction("addSavePasswordProgressLog", args);
 }
 
 void PasswordManagerInternalsUIIOS::PageLoaded(
@@ -87,7 +88,9 @@
           browser_state);
   // No service means the WebUI is displayed in Incognito.
   base::Value is_incognito(!service);
-  web_ui()->CallJavascriptFunction("notifyAboutIncognito", is_incognito);
+
+  std::vector<const base::Value*> args{&is_incognito};
+  web_ui()->CallJavascriptFunction("notifyAboutIncognito", args);
 
   if (service) {
     registered_with_logging_service_ = true;
diff --git a/ios/chrome/browser/ui/webui/signin_internals_ui_ios.cc b/ios/chrome/browser/ui/webui/signin_internals_ui_ios.cc
index ebc046f3..2407733 100644
--- a/ios/chrome/browser/ui/webui/signin_internals_ui_ios.cc
+++ b/ios/chrome/browser/ui/webui/signin_internals_ui_ios.cc
@@ -71,10 +71,10 @@
     // empty in incognito mode. Alternatively, we could force about:signin to
     // open in non-incognito mode always (like about:settings for ex.).
     if (about_signin_internals) {
-      const std::string& reply_handler =
-          "chrome.signin.getSigninInfo.handleReply";
+      base::Value status = about_signin_internals->GetSigninStatus()->Clone();
+      std::vector<const base::Value*> args{&status};
       web_ui()->CallJavascriptFunction(
-          reply_handler, *about_signin_internals->GetSigninStatus());
+          "chrome.signin.getSigninInfo.handleReply", args);
       std::vector<gaia::ListedAccount> cookie_accounts;
       GaiaCookieManagerService* cookie_manager_service =
           ios::GaiaCookieManagerServiceFactory::GetForBrowserState(
@@ -95,12 +95,14 @@
 
 void SignInInternalsUIIOS::OnSigninStateChanged(
     const base::DictionaryValue* info) {
-  const std::string& event_handler = "chrome.signin.onSigninInfoChanged.fire";
-  web_ui()->CallJavascriptFunction(event_handler, *info);
+  std::vector<const base::Value*> args{info};
+  web_ui()->CallJavascriptFunction("chrome.signin.onSigninInfoChanged.fire",
+                                   args);
 }
 
 void SignInInternalsUIIOS::OnCookieAccountsFetched(
     const base::DictionaryValue* info) {
+  std::vector<const base::Value*> args{info};
   web_ui()->CallJavascriptFunction("chrome.signin.onCookieAccountsFetched.fire",
-                                   *info);
+                                   args);
 }
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
index c6181a2f..59430783 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
+++ b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
@@ -266,8 +266,11 @@
     int request_id,
     std::unique_ptr<base::ListValue> nodes) {
   base::Value id(request_id);
+  base::Value nodes_clone = nodes->Clone();
+
+  std::vector<const base::Value*> args{&id, &nodes_clone};
   web_ui()->CallJavascriptFunction(syncer::sync_ui_util::kGetAllNodesCallback,
-                                   id, *nodes);
+                                   args);
 }
 
 void SyncInternalsMessageHandler::OnStateChanged(syncer::SyncService* sync) {
@@ -337,6 +340,9 @@
 void SyncInternalsMessageHandler::DispatchEvent(
     const std::string& name,
     const base::Value& details_value) {
-  web_ui()->CallJavascriptFunction(syncer::sync_ui_util::kDispatchEvent,
-                                   base::Value(name), details_value);
+  base::Value event_name = base::Value(name);
+
+  std::vector<const base::Value*> args{&event_name, &details_value};
+
+  web_ui()->CallJavascriptFunction(syncer::sync_ui_util::kDispatchEvent, args);
 }
diff --git a/ios/chrome/browser/ui/webui/user_actions_handler.mm b/ios/chrome/browser/ui/webui/user_actions_handler.mm
index 60ddff16..5c051d9 100644
--- a/ios/chrome/browser/ui/webui/user_actions_handler.mm
+++ b/ios/chrome/browser/ui/webui/user_actions_handler.mm
@@ -27,6 +27,6 @@
 
 void UserActionsHandler::OnUserAction(const std::string& action) {
   base::Value user_action_name(action);
-  web_ui()->CallJavascriptFunction("userActions.observeUserAction",
-                                   user_action_name);
+  std::vector<const base::Value*> args{&user_action_name};
+  web_ui()->CallJavascriptFunction("userActions.observeUserAction", args);
 }
diff --git a/ios/chrome/browser/ui/webui/version_handler.cc b/ios/chrome/browser/ui/webui/version_handler.cc
index 34a5daed..f4fa09b 100644
--- a/ios/chrome/browser/ui/webui/version_handler.cc
+++ b/ios/chrome/browser/ui/webui/version_handler.cc
@@ -25,6 +25,7 @@
 
 void VersionHandler::HandleRequestVersionInfo(const base::ListValue* args) {
   // Respond with the variations info immediately.
-  web_ui()->CallJavascriptFunction(version_ui::kReturnVariationInfo,
-                                   *version_ui::GetVariationsList());
+  base::Value variations_list = version_ui::GetVariationsList()->Clone();
+  std::vector<const base::Value*> params{&variations_list};
+  web_ui()->CallJavascriptFunction(version_ui::kReturnVariationInfo, params);
 }
diff --git a/ios/chrome/test/app/sync_test_util.mm b/ios/chrome/test/app/sync_test_util.mm
index 4e0272bd..ae27b3a 100644
--- a/ios/chrome/test/app/sync_test_util.mm
+++ b/ios/chrome/test/app/sync_test_util.mm
@@ -17,6 +17,7 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/core/service_access_type.h"
+#include "components/sync/device_info/device_info.h"
 #include "components/sync/device_info/device_info_sync_service.h"
 #include "components/sync/device_info/local_device_info_provider.h"
 #include "components/sync/driver/sync_service.h"
@@ -168,7 +169,7 @@
       DeviceInfoSyncServiceFactory::GetForBrowserState(browser_state);
   const syncer::LocalDeviceInfoProvider* info_provider =
       service->GetLocalDeviceInfoProvider();
-  return info_provider->GetLocalSyncCacheGUID();
+  return info_provider->GetLocalDeviceInfo()->guid();
 }
 
 void InjectAutofillProfileOnFakeSyncServer(std::string guid,
diff --git a/media/gpu/android/avda_codec_image.cc b/media/gpu/android/avda_codec_image.cc
index 9d9509a..fd181d2 100644
--- a/media/gpu/android/avda_codec_image.cc
+++ b/media/gpu/android/avda_codec_image.cc
@@ -36,7 +36,12 @@
   return GL_RGBA;
 }
 
+AVDACodecImage::BindOrCopy AVDACodecImage::ShouldBindOrCopy() {
+  return COPY;
+}
+
 bool AVDACodecImage::BindTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
diff --git a/media/gpu/android/avda_codec_image.h b/media/gpu/android/avda_codec_image.h
index e271cc9..c3011e7c 100644
--- a/media/gpu/android/avda_codec_image.h
+++ b/media/gpu/android/avda_codec_image.h
@@ -31,6 +31,7 @@
   // gl::GLImage implementation
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 6554f97..9ce0109 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -61,18 +61,24 @@
   return GL_RGBA;
 }
 
-bool CodecImage::BindTexImage(unsigned target) {
+CodecImage::BindOrCopy CodecImage::ShouldBindOrCopy() {
   // If we're using an overlay, then pretend it's bound.  That way, we'll get
-  // calls to ScheduleOverlayPlane.  Otherwise, fail so that we will be asked
-  // to CopyTexImage.  Note that we could just CopyTexImage here.
-  return !texture_owner_;
+  // calls to ScheduleOverlayPlane.  Otherwise, CopyTexImage needs to be called.
+  return !texture_owner_ ? BIND : COPY;
+}
+
+bool CodecImage::BindTexImage(unsigned target) {
+  DCHECK_EQ(BIND, ShouldBindOrCopy());
+  return true;
 }
 
 void CodecImage::ReleaseTexImage(unsigned target) {}
 
 bool CodecImage::CopyTexImage(unsigned target) {
   TRACE_EVENT0("media", "CodecImage::CopyTexImage");
-  if (!texture_owner_ || target != GL_TEXTURE_EXTERNAL_OES)
+  DCHECK_EQ(COPY, ShouldBindOrCopy());
+
+  if (target != GL_TEXTURE_EXTERNAL_OES)
     return false;
 
   GLint bound_service_id = 0;
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index b99deb7..8aaeaa6 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -42,6 +42,7 @@
   // gl::GLImage implementation
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 061b190..3271bfe 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -121,7 +121,7 @@
 
 TEST_F(CodecImageTest, CopyTexImageIsInvalidForOverlayImages) {
   auto i = NewImage(kOverlay);
-  ASSERT_FALSE(i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES));
+  ASSERT_NE(gl::GLImage::COPY, i->ShouldBindOrCopy());
 }
 
 TEST_F(CodecImageTest, ScheduleOverlayPlaneIsInvalidForTextureOwnerImages) {
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
index cdd47320..01ce444 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -464,8 +464,8 @@
     {VP9PROFILE_MIN, false /* decode_using_client_picture_buffers */},
     {VP9PROFILE_MIN, true /* decode_using_client_picture_buffers */}};
 
-INSTANTIATE_TEST_CASE_P(/* No prefix. */,
-                        VaapiVideoDecodeAcceleratorTest,
-                        ValuesIn(kTestCases));
+INSTANTIATE_TEST_SUITE_P(/* No prefix. */,
+                         VaapiVideoDecodeAcceleratorTest,
+                         ValuesIn(kTestCases));
 
 }  // namespace media
diff --git a/media/gpu/windows/dxva_picture_buffer_win.cc b/media/gpu/windows/dxva_picture_buffer_win.cc
index 7f647a5..bef6ac5 100644
--- a/media/gpu/windows/dxva_picture_buffer_win.cc
+++ b/media/gpu/windows/dxva_picture_buffer_win.cc
@@ -34,11 +34,15 @@
   // gl::GLImage implementation.
   gfx::Size GetSize() override { return size_; }
   unsigned GetInternalFormat() override { return GL_BGRA_EXT; }
+  BindOrCopy ShouldBindOrCopy() override { return BIND; }
   // PbufferPictureBuffer::CopySurfaceComplete does the actual binding, so
   // this doesn't do anything and always succeeds.
   bool BindTexImage(unsigned target) override { return true; }
   void ReleaseTexImage(unsigned target) override {}
-  bool CopyTexImage(unsigned target) override { return false; }
+  bool CopyTexImage(unsigned target) override {
+    NOTREACHED();
+    return false;
+  }
   bool CopyTexSubImage(unsigned target,
                        const gfx::Point& offset,
                        const gfx::Rect& rect) override {
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc
index d6ebfa00..c641e66 100644
--- a/mojo/core/channel.cc
+++ b/mojo/core/channel.cc
@@ -279,6 +279,8 @@
   std::vector<PlatformHandleInTransit> handles(num_handles);
   for (size_t i = 0; i < num_handles; i++) {
     HANDLE handle = base::win::Uint32ToHandle(message->handles_[i].handle);
+    if (PlatformHandleInTransit::IsPseudoHandle(handle))
+      return nullptr;
     if (from_process == base::kNullProcessHandle) {
       handles[i] = PlatformHandleInTransit(
           PlatformHandle(base::win::ScopedHandle(handle)));
diff --git a/mojo/core/channel_win.cc b/mojo/core/channel_win.cc
index 0a2000c2..a927dbb 100644
--- a/mojo/core/channel_win.cc
+++ b/mojo/core/channel_win.cc
@@ -121,6 +121,8 @@
     for (size_t i = 0; i < num_handles; i++) {
       HANDLE handle_value =
           base::win::Uint32ToHandle(extra_header_handles[i].handle);
+      if (PlatformHandleInTransit::IsPseudoHandle(handle_value))
+        return false;
       if (remote_process().is_valid()) {
         // If we know the remote process's handle, we assume it doesn't know
         // ours; that means any handle values still belong to that process, and
diff --git a/mojo/core/multiprocess_message_pipe_unittest.cc b/mojo/core/multiprocess_message_pipe_unittest.cc
index a30f376dd..ea06608 100644
--- a/mojo/core/multiprocess_message_pipe_unittest.cc
+++ b/mojo/core/multiprocess_message_pipe_unittest.cc
@@ -483,13 +483,13 @@
 
 // Android multi-process tests are not executing the new process. This is flaky.
 #if !defined(OS_ANDROID)
-INSTANTIATE_TEST_CASE_P(PipeCount,
-                        MultiprocessMessagePipeTestWithPipeCount,
-                        // TODO(rockot): Enable the 128 and 250 pipe cases when
-                        // ChannelPosix and ChannelFuchsia have support for
-                        // sending larger numbers of handles per-message. See
-                        // kMaxAttachedHandles in channel.cc for details.
-                        testing::Values(1u, 64u /*, 128u, 250u*/));
+INSTANTIATE_TEST_SUITE_P(PipeCount,
+                         MultiprocessMessagePipeTestWithPipeCount,
+                         // TODO(rockot): Enable the 128 and 250 pipe cases when
+                         // ChannelPosix and ChannelFuchsia have support for
+                         // sending larger numbers of handles per-message. See
+                         // kMaxAttachedHandles in channel.cc for details.
+                         testing::Values(1u, 64u /*, 128u, 250u*/));
 #endif
 
 DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
@@ -1335,7 +1335,7 @@
   EXPECT_EQ("bye", ReadMessage(parent));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     MultiprocessMessagePipeTestWithPeerSupport,
     testing::Values(test::MojoTestBase::LaunchType::CHILD,
diff --git a/mojo/core/platform_handle_in_transit.cc b/mojo/core/platform_handle_in_transit.cc
index 7b82f27..9ee60d86 100644
--- a/mojo/core/platform_handle_in_transit.cc
+++ b/mojo/core/platform_handle_in_transit.cc
@@ -129,6 +129,21 @@
 
 #if defined(OS_WIN)
 // static
+bool PlatformHandleInTransit::IsPseudoHandle(HANDLE handle) {
+  // Note that there appears to be no official documentation covering the
+  // existence of specific pseudo handle values. In practice it's clear that
+  // e.g. -1 is the current process, -2 is the current thread, etc. The largest
+  // negative value known to be an issue with DuplicateHandle in the fuzzer is
+  // -12.
+  //
+  // Note that there is virtually no risk of a real handle value falling within
+  // this range and being misclassified as a pseudo handle.
+  constexpr int kMinimumKnownPseudoHandleValue = -12;
+  const auto value = static_cast<int32_t>(reinterpret_cast<uintptr_t>(handle));
+  return value < 0 && value >= kMinimumKnownPseudoHandleValue;
+}
+
+// static
 PlatformHandle PlatformHandleInTransit::TakeIncomingRemoteHandle(
     HANDLE handle,
     base::ProcessHandle owning_process) {
diff --git a/mojo/core/platform_handle_in_transit.h b/mojo/core/platform_handle_in_transit.h
index b75a01fb..ebe49e26 100644
--- a/mojo/core/platform_handle_in_transit.h
+++ b/mojo/core/platform_handle_in_transit.h
@@ -62,6 +62,26 @@
 #if defined(OS_WIN)
   HANDLE remote_handle() const { return remote_handle_; }
 
+  // Indicates whether |handle| is a known pseudo handle value. In a fuzzing
+  // environment we merely simulate IPC, so we end up accepting "remote" handle
+  // values from our own process. This means that unlike in production
+  // scenarios, we may end up successfully calling DuplicateHandle on a fuzzed
+  // pseudo handle value (in production if a remote process sent us a pseudo
+  // handle value, DuplicateHandle would always fail).
+  //
+  // For some reason, a small number of special pseudo handle values always
+  // duplicate to the same real handle value when DUPLICATE_CLOSE_SOURCE is
+  // specified, presumably because the returned handle is closed before it's
+  // even returned. For example, duplicating -10 with DUPLICATE_CLOSE_SOURCE
+  // always yields the handle value 0x50. This ends up interacting poorly with
+  // the rest of Mojo's handle deserialization code and eventually crashes
+  // in ScopedHandleVerifier.
+  //
+  // We avoid the issue by explicitly discarding any known pseudo handle values,
+  // since they are always invalid when received from a remote process anyway
+  // and thus always signal a misbehaving client.
+  static bool IsPseudoHandle(HANDLE handle);
+
   // Returns a new local handle, with ownership of |handle| being transferred
   // from |owning_process| to the caller.
   static PlatformHandle TakeIncomingRemoteHandle(
diff --git a/mojo/public/cpp/bindings/tests/binding_set_unittest.cc b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
index 99551583..434d1e2 100644
--- a/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
@@ -643,7 +643,7 @@
   EXPECT_EQ(0, PingInstanceCounter::instance_count);
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(BindingSetTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(BindingSetTest);
 
 }  // namespace
 }  // namespace test
diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc
index ad985c7..b9d620b 100644
--- a/mojo/public/cpp/bindings/tests/binding_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc
@@ -652,8 +652,8 @@
   run_loop.Run();
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(BindingTest);
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(StrongBindingTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(BindingTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(StrongBindingTest);
 
 }  // namespace
 }  // mojo
diff --git a/mojo/public/cpp/bindings/tests/bindings_test_base.h b/mojo/public/cpp/bindings/tests/bindings_test_base.h
index 5f5c779..32ddca1 100644
--- a/mojo/public/cpp/bindings/tests/bindings_test_base.h
+++ b/mojo/public/cpp/bindings/tests/bindings_test_base.h
@@ -40,8 +40,8 @@
 
 }  // namespace mojo
 
-#define INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(fixture)                   \
-  INSTANTIATE_TEST_CASE_P(                                               \
+#define INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(fixture)                  \
+  INSTANTIATE_TEST_SUITE_P(                                              \
       , fixture,                                                         \
       testing::Values(                                                   \
           mojo::BindingsTestSerializationMode::kSerializeBeforeSend,     \
diff --git a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
index c0f29a2..f55312ad1 100644
--- a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -319,7 +319,7 @@
   EXPECT_EQ(std::string("object2"), name2);
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(HandlePassingTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(HandlePassingTest);
 
 }  // namespace
 }  // namespace test
diff --git a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
index d7dabca..51acb98 100644
--- a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -977,7 +977,7 @@
   thread_safe_ptr = nullptr;
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(InterfacePtrTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(InterfacePtrTest);
 
 }  // namespace
 }  // namespace test
diff --git a/mojo/public/cpp/bindings/tests/native_struct_unittest.cc b/mojo/public/cpp/bindings/tests/native_struct_unittest.cc
index bbdf8b6..95a549d 100644
--- a/mojo/public/cpp/bindings/tests/native_struct_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/native_struct_unittest.cc
@@ -93,6 +93,6 @@
   loop.Run();
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(NativeStructTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(NativeStructTest);
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc b/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc
index 290f13fe..bb48ab9 100644
--- a/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc
@@ -187,7 +187,7 @@
   EXPECT_TRUE(error);
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(ReportBadMessageTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(ReportBadMessageTest);
 
 }  // namespace
 }  // namespace test
diff --git a/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
index 317e43c..1369804 100644
--- a/mojo/public/cpp/bindings/tests/request_response_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
@@ -145,7 +145,7 @@
   EXPECT_EQ(sample::Enum::VALUE, value);
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(RequestResponseTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(RequestResponseTest);
 
 }  // namespace
 }  // namespace test
diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
index 34d8974..d6eb097 100644
--- a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -361,7 +361,7 @@
   EXPECT_EQ(-0x123456789, defaults->a25);
 }
 
-INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(BindingsSampleTest);
+INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(BindingsSampleTest);
 
 }  // namespace
 }  // namespace sample
diff --git a/mojo/public/cpp/platform/tests/platform_handle_unittest.cc b/mojo/public/cpp/platform/tests/platform_handle_unittest.cc
index 6c570fd..c3232e2 100644
--- a/mojo/public/cpp/platform/tests/platform_handle_unittest.cc
+++ b/mojo/public/cpp/platform/tests/platform_handle_unittest.cc
@@ -244,20 +244,20 @@
   EXPECT_EQ(kTestData, GetObjectContents(handle));
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        PlatformHandleTest,
+INSTANTIATE_TEST_SUITE_P(,
+                         PlatformHandleTest,
 #if defined(OS_WIN)
-                        testing::Values(HandleType::kHandle)
+                         testing::Values(HandleType::kHandle)
 #elif defined(OS_FUCHSIA)
-                        testing::Values(HandleType::kHandle,
-                                        HandleType::kFileDescriptor)
+                         testing::Values(HandleType::kHandle,
+                                         HandleType::kFileDescriptor)
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
-                        testing::Values(HandleType::kFileDescriptor,
-                                        HandleType::kMachPort)
+                         testing::Values(HandleType::kFileDescriptor,
+                                         HandleType::kMachPort)
 #elif defined(OS_POSIX)
-                        testing::Values(HandleType::kFileDescriptor)
+                         testing::Values(HandleType::kFileDescriptor)
 #endif
-                            );
+);
 
 }  // namespace
 }  // namespace mojo
diff --git a/mojo/public/cpp/system/tests/invitation_unittest.cc b/mojo/public/cpp/system/tests/invitation_unittest.cc
index 9c85816..138751d 100644
--- a/mojo/public/cpp/system/tests/invitation_unittest.cc
+++ b/mojo/public/cpp/system/tests/invitation_unittest.cc
@@ -322,14 +322,14 @@
   EXPECT_EQ(kDisconnectMessage, ReadMessage(pipe));
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        InvitationCppTest,
-                        testing::Values(TransportType::kChannel
+INSTANTIATE_TEST_SUITE_P(,
+                         InvitationCppTest,
+                         testing::Values(TransportType::kChannel
 #if !defined(OS_FUCHSIA)
-                                        ,
-                                        TransportType::kChannelServer
+                                         ,
+                                         TransportType::kChannelServer
 #endif
-                                        ));
+                                         ));
 
 }  // namespace
 }  // namespace mojo
diff --git a/native_client_sdk/src/tests/nacl_io_test/http_fs_test.cc b/native_client_sdk/src/tests/nacl_io_test/http_fs_test.cc
index 1ab079341..67b4a5b 100644
--- a/native_client_sdk/src/tests/nacl_io_test/http_fs_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/http_fs_test.cc
@@ -187,7 +187,7 @@
 }
 
 // Instantiate the above tests for all caching types.
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     Default,
     HttpFsTest,
     ::testing::Values((uint32_t)kStringMapParamCacheNone,
@@ -255,10 +255,10 @@
 // Instantiate the large file tests, only when cache content is off.
 // TODO(binji): make cache content smarter, so it doesn't try to cache enormous
 // files. See http://crbug.com/369279.
-INSTANTIATE_TEST_CASE_P(Default,
-                        HttpFsLargeFileTest,
-                        ::testing::Values((uint32_t)kStringMapParamCacheNone,
-                                          (uint32_t)kStringMapParamCacheStat));
+INSTANTIATE_TEST_SUITE_P(Default,
+                         HttpFsLargeFileTest,
+                         ::testing::Values((uint32_t)kStringMapParamCacheNone,
+                                           (uint32_t)kStringMapParamCacheStat));
 
 TEST(HttpFsDirTest, Root) {
   StringMap_t args;
diff --git a/net/BUILD.gn b/net/BUILD.gn
index c85a3b3..853c946 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1416,6 +1416,8 @@
       "third_party/quic/core/quic_packet_creator.h",
       "third_party/quic/core/quic_packet_generator.cc",
       "third_party/quic/core/quic_packet_generator.h",
+      "third_party/quic/core/quic_packet_number.cc",
+      "third_party/quic/core/quic_packet_number.h",
       "third_party/quic/core/quic_packet_writer.h",
       "third_party/quic/core/quic_packets.cc",
       "third_party/quic/core/quic_packets.h",
@@ -5179,6 +5181,7 @@
     "third_party/quic/core/quic_one_block_arena_test.cc",
     "third_party/quic/core/quic_packet_creator_test.cc",
     "third_party/quic/core/quic_packet_generator_test.cc",
+    "third_party/quic/core/quic_packet_number_test.cc",
     "third_party/quic/core/quic_received_packet_manager_test.cc",
     "third_party/quic/core/quic_sent_packet_manager_test.cc",
     "third_party/quic/core/quic_server_id_test.cc",
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 8a4bbd7..2fb6f3a 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -32,6 +32,7 @@
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/socks_connect_job.h"
+#include "net/socket/transport_client_socket_pool.h"
 #include "net/socket/transport_connect_job.h"
 #include "net/spdy/spdy_test_util_common.h"
 #include "net/test/gtest_util.h"
@@ -73,7 +74,18 @@
   HttpProxyClientSocketPoolTest()
       : transport_socket_pool_(kMaxSockets,
                                kMaxSocketsPerGroup,
-                               &socket_factory_),
+                               &socket_factory_,
+                               session_deps_.host_resolver.get(),
+                               session_deps_.cert_verifier.get(),
+                               session_deps_.channel_id_service.get(),
+                               session_deps_.transport_security_state.get(),
+                               session_deps_.cert_transparency_verifier.get(),
+                               session_deps_.ct_policy_enforcer.get(),
+                               std::string() /* ssl_session_cache_shard */,
+                               session_deps_.ssl_config_service.get(),
+                               nullptr /* socket_performance_watcher_factory */,
+                               nullptr /* network_quality_estimator */,
+                               nullptr /* net_log */),
         ssl_socket_pool_(kMaxSockets,
                          kMaxSocketsPerGroup,
                          session_deps_.cert_verifier.get(),
@@ -98,6 +110,7 @@
                                                         nullptr,
                                                         &estimator_,
                                                         nullptr)) {
+    session_deps_.host_resolver->set_synchronous_mode(true);
     session_ = CreateNetworkSession();
   }
 
@@ -226,19 +239,11 @@
     return SpdySessionDependencies::SpdyCreateSession(&session_deps_);
   }
 
-  RequestPriority GetLastTransportRequestPriority() const {
-    return transport_socket_pool_.last_request_priority();
-  }
-
-  RequestPriority GetTransportRequestPriority(size_t index) const {
-    return transport_socket_pool_.requests()[index]->priority();
-  }
-
   const base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
   TestNetworkQualityEstimator* estimator() { return &estimator_; }
 
-  MockTransportClientSocketPool* transport_socket_pool() {
+  TransportClientSocketPool* transport_socket_pool() {
     return &transport_socket_pool_;
   }
   SSLClientSocketPool* ssl_socket_pool() { return &ssl_socket_pool_; }
@@ -257,9 +262,7 @@
 
   TestNetworkQualityEstimator estimator_;
 
-  MockTransportClientSocketPool transport_socket_pool_;
-  MockHostResolver host_resolver_;
-  std::unique_ptr<CertVerifier> cert_verifier_;
+  TransportClientSocketPool transport_socket_pool_;
   SSLClientSocketPool ssl_socket_pool_;
 
   std::unique_ptr<HttpNetworkSession> session_;
@@ -352,21 +355,23 @@
 // Make sure that HttpProxyConnectJob passes on its priority to its
 // (non-SSL) socket request on Init.
 TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) {
-  Initialize(base::span<MockRead>(), base::span<MockWrite>(),
-             base::span<MockRead>(), base::span<MockWrite>());
-  EXPECT_EQ(OK, handle_.Init("a", CreateNoTunnelParams(), HIGHEST, SocketTag(),
-                             ClientSocketPool::RespectLimits::ENABLED,
-                             CompletionOnceCallback(), pool_.get(),
-                             NetLogWithSource()));
-  EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority());
-  EXPECT_EQ(HIGHEST, GetTransportRequestPriority(0));
+  // Make request hang during host resolution, so can observe priority there.
+  session_deps_.host_resolver->set_synchronous_mode(false);
+  session_deps_.host_resolver->set_ondemand_mode(true);
+
+  EXPECT_THAT(
+      handle_.Init("a", CreateNoTunnelParams(), HIGHEST, SocketTag(),
+                   ClientSocketPool::RespectLimits::ENABLED,
+                   CompletionOnceCallback(), pool_.get(), NetLogWithSource()),
+      IsError(ERR_IO_PENDING));
+  EXPECT_EQ(HIGHEST, session_deps_.host_resolver->last_request_priority());
 }
 
 TEST_P(HttpProxyClientSocketPoolTest, SetPriority) {
-  data_ = std::make_unique<SequencedSocketData>();
-  data_->set_connect_data(MockConnect(ASYNC, OK));
-
-  socket_factory()->AddSocketDataProvider(data_.get());
+  // Make request hang during host resolution, so can observe priority changes
+  // there.
+  session_deps_.host_resolver->set_synchronous_mode(false);
+  session_deps_.host_resolver->set_ondemand_mode(true);
 
   int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(),
                         ClientSocketPool::RespectLimits::ENABLED,
@@ -375,10 +380,20 @@
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
 
-  EXPECT_EQ(LOW, GetTransportRequestPriority(0));
+  // For HTTPS requests, the H2 code will attempt to resolve the host name from
+  // the cache before the ConnectJob gets stuck trying to resolve the host name.
+  // Simplest to just add up total number of resolves to figure the resolution
+  // request being waited on.
+  int host_resolve_request_id =
+      session_deps_.host_resolver->num_resolve() +
+      session_deps_.host_resolver->num_resolve_from_cache();
+
+  EXPECT_EQ(LOW, session_deps_.host_resolver->request_priority(
+                     host_resolve_request_id));
 
   handle_.SetPriority(HIGHEST);
-  EXPECT_EQ(HIGHEST, GetTransportRequestPriority(0));
+  EXPECT_EQ(HIGHEST, session_deps_.host_resolver->request_priority(
+                         host_resolve_request_id));
 }
 
 TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) {
@@ -525,8 +540,6 @@
       handle_.Init("a", CreateTunnelParams(), MEDIUM, SocketTag(),
                    ClientSocketPool::RespectLimits::ENABLED,
                    callback_.callback(), pool_.get(), NetLogWithSource()));
-  EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority());
-  EXPECT_EQ(MEDIUM, GetTransportRequestPriority(0));
 
   handle_.SetPriority(HIGHEST);
   // Expect frame with HIGHEST priority, not MEDIUM.
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc
index 3e3d842..153491a 100644
--- a/net/http/http_proxy_client_socket_wrapper_unittest.cc
+++ b/net/http/http_proxy_client_socket_wrapper_unittest.cc
@@ -161,13 +161,13 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructSettingsPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return client_maker_.MakeInitialSettingsPacket(packet_number,
                                                    &header_stream_offset_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectRequestPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       RequestPriority priority) {
     spdy::SpdyHeaderBlock block;
     PopulateConnectRequestIR(&block);
@@ -178,7 +178,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerConnectReplyPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin) {
     spdy::SpdyHeaderBlock block;
     block[":status"] = "200";
@@ -189,11 +189,11 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckAndRstPacket(
         packet_number, !kIncludeVersion, client_data_stream_id1_, error_code,
         largest_received, smallest_received, least_unacked, kSendFeedback);
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 882c04e..119c7e7 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -548,7 +548,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -562,7 +562,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -574,7 +574,7 @@
   // Construct a data packet with multiple data frames
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructClientMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -589,7 +589,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       RequestPriority request_priority,
       size_t* spdy_headers_frame_length) {
@@ -599,7 +599,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestHeadersPacketInner(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool fin,
       RequestPriority request_priority,
@@ -611,7 +611,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestHeadersPacketInner(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool fin,
       RequestPriority request_priority,
@@ -632,7 +632,7 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructRequestHeadersAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       RequestPriority request_priority,
       quic::QuicStreamOffset* header_stream_offset,
@@ -651,7 +651,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       spdy::SpdyHeaderBlock response_headers,
       size_t* spdy_headers_frame_length,
@@ -662,7 +662,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseHeadersPacketInner(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool fin,
       spdy::SpdyHeaderBlock response_headers,
@@ -674,7 +674,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseTrailersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       spdy::SpdyHeaderBlock trailers,
       size_t* spdy_headers_frame_length,
@@ -685,25 +685,25 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientRstStreamPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return ConstructRstStreamCancelledPacket(packet_number, !kIncludeVersion, 0,
                                              &client_maker_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerRstStreamPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return ConstructRstStreamCancelledPacket(packet_number, !kIncludeVersion, 0,
                                              &server_maker_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientEarlyRstStreamPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return ConstructRstStreamCancelledPacket(packet_number, kIncludeVersion, 0,
                                              &client_maker_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRstStreamCancelledPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version,
       size_t bytes_written,
       QuicTestPacketMaker* maker) {
@@ -716,10 +716,10 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructClientAckAndRstStreamPacket(quic::QuicPacketNumber packet_number,
-                                       quic::QuicPacketNumber largest_received,
-                                       quic::QuicPacketNumber smallest_received,
-                                       quic::QuicPacketNumber least_unacked) {
+  ConstructClientAckAndRstStreamPacket(uint64_t packet_number,
+                                       uint64_t largest_received,
+                                       uint64_t smallest_received,
+                                       uint64_t least_unacked) {
     return client_maker_.MakeAckAndRstPacket(
         packet_number, !kIncludeVersion, stream_id_,
         quic::QUIC_STREAM_CANCELLED, largest_received, smallest_received,
@@ -727,11 +727,11 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data,
@@ -747,11 +747,11 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructAckAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       const std::vector<std::string> data_writes) {
@@ -765,27 +765,27 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked,
                                        !kIncludeCongestionFeedback);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return server_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked,
                                        !kIncludeCongestionFeedback);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset* offset) {
     return client_maker_.MakeInitialSettingsPacket(packet_number, offset);
   }
diff --git a/net/quic/mock_decrypter.cc b/net/quic/mock_decrypter.cc
index 347efe3..717797b 100644
--- a/net/quic/mock_decrypter.cc
+++ b/net/quic/mock_decrypter.cc
@@ -40,7 +40,7 @@
 }
 
 bool MockDecrypter::DecryptPacket(QuicTransportVersion version,
-                                  QuicPacketNumber /*packet_number*/,
+                                  uint64_t /*packet_number*/,
                                   QuicStringPiece associated_data,
                                   QuicStringPiece ciphertext,
                                   char* output,
diff --git a/net/quic/mock_decrypter.h b/net/quic/mock_decrypter.h
index adb818c..0466a49 100644
--- a/net/quic/mock_decrypter.h
+++ b/net/quic/mock_decrypter.h
@@ -34,7 +34,7 @@
   bool SetDiversificationNonce(
       const quic::DiversificationNonce& nonce) override;
   bool DecryptPacket(quic::QuicTransportVersion version,
-                     quic::QuicPacketNumber packet_number,
+                     uint64_t packet_number,
                      quic::QuicStringPiece associated_data,
                      quic::QuicStringPiece ciphertext,
                      char* output,
diff --git a/net/quic/mock_encrypter.cc b/net/quic/mock_encrypter.cc
index d4f7e536..333b3e4 100644
--- a/net/quic/mock_encrypter.cc
+++ b/net/quic/mock_encrypter.cc
@@ -30,7 +30,7 @@
 }
 
 bool MockEncrypter::EncryptPacket(QuicTransportVersion version,
-                                  QuicPacketNumber /*packet_number*/,
+                                  uint64_t /*packet_number*/,
                                   QuicStringPiece associated_data,
                                   QuicStringPiece plaintext,
                                   char* output,
diff --git a/net/quic/mock_encrypter.h b/net/quic/mock_encrypter.h
index 786babfc..133a751 100644
--- a/net/quic/mock_encrypter.h
+++ b/net/quic/mock_encrypter.h
@@ -29,7 +29,7 @@
   bool SetNoncePrefix(quic::QuicStringPiece nonce_prefix) override;
   bool SetIV(quic::QuicStringPiece iv) override;
   bool EncryptPacket(quic::QuicTransportVersion version,
-                     quic::QuicPacketNumber packet_number,
+                     uint64_t packet_number,
                      quic::QuicStringPiece associated_data,
                      quic::QuicStringPiece plaintext,
                      char* output,
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index c10dd4f9..6f1f0589 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -58,7 +58,7 @@
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   dict->SetInteger("transmission_type", transmission_type);
   dict->SetKey("packet_number",
-               NetLogNumberValue(serialized_packet.packet_number));
+               NetLogNumberValue(serialized_packet.packet_number.ToUint64()));
   dict->SetInteger("size", serialized_packet.encrypted_length);
   dict->SetKey("sent_time_us", NetLogNumberValue(sent_time.ToDebuggingValue()));
   return std::move(dict);
@@ -69,8 +69,10 @@
     quic::QuicPacketNumber new_packet_number,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("old_packet_number", NetLogNumberValue(old_packet_number));
-  dict->SetKey("new_packet_number", NetLogNumberValue(new_packet_number));
+  dict->SetKey("old_packet_number",
+               NetLogNumberValue(old_packet_number.ToUint64()));
+  dict->SetKey("new_packet_number",
+               NetLogNumberValue(new_packet_number.ToUint64()));
   return std::move(dict);
 }
 
@@ -81,7 +83,7 @@
     NetLogCaptureMode /*capture_mode*/) {
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetInteger("transmission_type", transmission_type);
-  dict->SetKey("packet_number", NetLogNumberValue(packet_number));
+  dict->SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
   dict->SetKey("detection_time_us",
                NetLogNumberValue(detection_time.ToDebuggingValue()));
   return dict;
@@ -91,7 +93,7 @@
     quic::QuicPacketNumber packet_number,
     NetLogCaptureMode /* capture_mode */) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("packet_number", NetLogNumberValue(packet_number));
+  dict->SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
   return std::move(dict);
 }
 
@@ -103,7 +105,8 @@
                   header->destination_connection_id.ToString());
   dict->SetInteger("reset_flag", header->reset_flag);
   dict->SetInteger("version_flag", header->version_flag);
-  dict->SetKey("packet_number", NetLogNumberValue(header->packet_number));
+  dict->SetKey("packet_number",
+               NetLogNumberValue(header->packet_number.ToUint64()));
   return std::move(dict);
 }
 
@@ -122,7 +125,8 @@
     const quic::QuicAckFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
   auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetKey("largest_observed", NetLogNumberValue(frame->largest_acked));
+  dict->SetKey("largest_observed",
+               NetLogNumberValue(frame->largest_acked.ToUint64()));
   dict->SetKey("delta_time_largest_observed_us",
                NetLogNumberValue(frame->ack_delay_time.ToMicroseconds()));
 
@@ -133,7 +137,7 @@
     for (quic::QuicPacketNumber packet = frame->packets.Min();
          packet < frame->largest_acked; ++packet) {
       if (!frame->packets.Contains(packet)) {
-        missing->GetList().push_back(NetLogNumberValue(packet));
+        missing->GetList().push_back(NetLogNumberValue(packet.ToUint64()));
       }
     }
   }
@@ -143,7 +147,7 @@
   const quic::PacketTimeVector& received_times = frame->received_packet_times;
   for (auto it = received_times.begin(); it != received_times.end(); ++it) {
     auto info = std::make_unique<base::DictionaryValue>();
-    info->SetKey("packet_number", NetLogNumberValue(it->first));
+    info->SetKey("packet_number", NetLogNumberValue(it->first.ToUint64()));
     info->SetKey("received", NetLogNumberValue(it->second.ToDebuggingValue()));
     received->Append(std::move(info));
   }
@@ -202,7 +206,8 @@
     NetLogCaptureMode /* capture_mode */) {
   auto dict = std::make_unique<base::DictionaryValue>();
   auto sent_info = std::make_unique<base::DictionaryValue>();
-  sent_info->SetKey("least_unacked", NetLogNumberValue(frame->least_unacked));
+  sent_info->SetKey("least_unacked",
+                    NetLogNumberValue(frame->least_unacked.ToUint64()));
   dict->Set("sent_info", std::move(sent_info));
   return std::move(dict);
 }
@@ -295,11 +300,9 @@
     const NetLogWithSource& net_log)
     : net_log_(net_log),
       session_(session),
-      last_received_packet_number_(0),
       last_received_packet_size_(0),
       no_packet_received_after_ping_(false),
       previous_received_packet_size_(0),
-      largest_received_packet_number_(0),
       num_out_of_order_received_packets_(0),
       num_out_of_order_large_received_packets_(0),
       num_packets_received_(0),
@@ -508,7 +511,7 @@
     quic::QuicTime sent_time) {
   if (!net_log_is_capturing_)
     return;
-  if (original_packet_number == 0) {
+  if (!original_packet_number.IsInitialized()) {
     net_log_.AddEvent(
         NetLogEventType::QUIC_SESSION_PACKET_SENT,
         base::Bind(&NetLogQuicPacketSentCallback, serialized_packet,
@@ -596,9 +599,14 @@
 void QuicConnectionLogger::OnPacketHeader(
     const quic::QuicPacketHeader& header) {
   ++num_packets_received_;
-  if (largest_received_packet_number_ < header.packet_number) {
-    quic::QuicPacketNumber delta =
-        header.packet_number - largest_received_packet_number_;
+  if (!largest_received_packet_number_.IsInitialized() ||
+      largest_received_packet_number_ < header.packet_number) {
+    // TODO(fayang): Fix this as this check assume the first received packet
+    // is 1.
+    uint64_t delta = header.packet_number.ToUint64();
+    if (largest_received_packet_number_.IsInitialized()) {
+      delta = header.packet_number - largest_received_packet_number_;
+    }
     if (delta > 1) {
       // There is a gap between the largest packet previously received and
       // the current packet.  This indicates either loss, or out-of-order
@@ -609,10 +617,13 @@
     }
     largest_received_packet_number_ = header.packet_number;
   }
-  if (header.packet_number < received_packets_.size()) {
-    received_packets_[static_cast<size_t>(header.packet_number)] = true;
+  // TODO(fayang): Fix this as this check assume the first received packet is 1.
+  if (header.packet_number < quic::QuicPacketNumber(received_packets_.size())) {
+    received_packets_[static_cast<size_t>(header.packet_number.ToUint64())] =
+        true;
   }
-  if (header.packet_number < last_received_packet_number_) {
+  if (last_received_packet_number_.IsInitialized() &&
+      header.packet_number < last_received_packet_number_) {
     ++num_out_of_order_received_packets_;
     if (previous_received_packet_size_ < last_received_packet_size_)
       ++num_out_of_order_large_received_packets_;
@@ -621,10 +632,18 @@
         static_cast<base::HistogramBase::Sample>(last_received_packet_number_ -
                                                  header.packet_number));
   } else if (no_packet_received_after_ping_) {
-    UMA_HISTOGRAM_COUNTS_1M(
-        "Net.QuicSession.PacketGapReceivedNearPing",
-        static_cast<base::HistogramBase::Sample>(header.packet_number -
-                                                 last_received_packet_number_));
+    if (!last_received_packet_number_.IsInitialized()) {
+      // TODO(fayang): Fix this as this check assume the first received packet
+      // is 1.
+      UMA_HISTOGRAM_COUNTS_1M("Net.QuicSession.PacketGapReceivedNearPing",
+                              static_cast<base::HistogramBase::Sample>(
+                                  header.packet_number.ToUint64()));
+    } else {
+      UMA_HISTOGRAM_COUNTS_1M(
+          "Net.QuicSession.PacketGapReceivedNearPing",
+          static_cast<base::HistogramBase::Sample>(
+              header.packet_number - last_received_packet_number_));
+    }
     no_packet_received_after_ping_ = false;
   }
   last_received_packet_number_ = header.packet_number;
@@ -642,9 +661,12 @@
 
 void QuicConnectionLogger::OnAckFrame(const quic::QuicAckFrame& frame) {
   const size_t kApproximateLargestSoloAckBytes = 100;
-  if (last_received_packet_number_ < received_acks_.size() &&
+  // TODO(fayang): Fix this as this check assume the first received packet is 1.
+  if (last_received_packet_number_ <
+          quic::QuicPacketNumber(received_acks_.size()) &&
       last_received_packet_size_ < kApproximateLargestSoloAckBytes) {
-    received_acks_[static_cast<size_t>(last_received_packet_number_)] = true;
+    received_acks_[static_cast<size_t>(
+        last_received_packet_number_.ToUint64())] = true;
   }
 
   if (!net_log_is_capturing_)
@@ -823,10 +845,14 @@
 }
 
 float QuicConnectionLogger::ReceivedPacketLossRate() const {
-  if (largest_received_packet_number_ <= num_packets_received_)
+  // TODO(fayang): Fix this as this check assume the first received packet is 1.
+  if (!largest_received_packet_number_.IsInitialized() ||
+      largest_received_packet_number_ <=
+          quic::QuicPacketNumber(num_packets_received_))
     return 0.0f;
-  float num_received = largest_received_packet_number_ - num_packets_received_;
-  return num_received / largest_received_packet_number_;
+  float num_received =
+      largest_received_packet_number_.ToUint64() - num_packets_received_;
+  return num_received / largest_received_packet_number_.ToUint64();
 }
 
 void QuicConnectionLogger::OnRttChanged(quic::QuicTime::Delta rtt) const {
@@ -849,7 +875,9 @@
   // histogram.  (e.g., if we only got 5 packets, but lost 1, we'd otherwise
   // record a 20% loss in this histogram!). We may still get some strange data
   // (1 loss in 22 is still high :-/).
-  if (largest_received_packet_number_ <= 21)
+  // TODO(fayang): Fix this as this check assume the first received packet is 1.
+  if (!largest_received_packet_number_.IsInitialized() ||
+      largest_received_packet_number_ <= quic::QuicPacketNumber(21))
     return;
 
   string prefix("Net.QuicSession.PacketLossRate_");
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index b414c978..5b8049b3 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -368,7 +368,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> InnerConstructDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -380,7 +380,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -392,7 +392,7 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructClientMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -402,7 +402,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
@@ -413,7 +413,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> InnerConstructRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -426,7 +426,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> InnerConstructRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -444,7 +444,7 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructRequestHeadersAndDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -462,7 +462,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestAndRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -482,7 +482,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       RequestPriority request_priority,
       size_t* spdy_headers_frame_length) {
@@ -492,7 +492,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> InnerConstructResponseHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool fin,
       size_t* spdy_headers_frame_length) {
@@ -503,7 +503,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       size_t* spdy_headers_frame_length) {
     return InnerConstructResponseHeadersPacket(packet_number, stream_id_, fin,
@@ -511,7 +511,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructResponseHeadersPacketWithOffset(quic::QuicPacketNumber packet_number,
+  ConstructResponseHeadersPacketWithOffset(uint64_t packet_number,
                                            bool fin,
                                            size_t* spdy_headers_frame_length,
                                            quic::QuicStreamOffset* offset) {
@@ -521,7 +521,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructResponseTrailersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin,
       spdy::SpdyHeaderBlock trailers,
       size_t* spdy_headers_frame_length,
@@ -532,21 +532,19 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientRstStreamPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return client_maker_.MakeRstPacket(packet_number, true, stream_id_,
                                        quic::QUIC_RST_ACKNOWLEDGEMENT);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructClientRstStreamCancelledPacket(
-      quic::QuicPacketNumber packet_number) {
+  ConstructClientRstStreamCancelledPacket(uint64_t packet_number) {
     return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
                                        stream_id_, quic::QUIC_STREAM_CANCELLED);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructClientRstStreamVaryMismatchPacket(
-      quic::QuicPacketNumber packet_number) {
+  ConstructClientRstStreamVaryMismatchPacket(uint64_t packet_number) {
     return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
                                        promise_id_,
                                        quic::QUIC_PROMISE_VARY_MISMATCH);
@@ -554,7 +552,7 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructClientRstStreamVaryMismatchAndRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -572,10 +570,10 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstStreamPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckAndRstPacket(
         packet_number, !kIncludeVersion, stream_id_,
         quic::QUIC_STREAM_CANCELLED, largest_received, smallest_received,
@@ -583,7 +581,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientRstStreamErrorPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version) {
     return client_maker_.MakeRstPacket(packet_number, include_version,
                                        stream_id_,
@@ -591,32 +589,32 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstStreamPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return ConstructAckAndRstStreamPacket(packet_number, 2, 1, 2);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked,
                                        !kIncludeCongestionFeedback);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return server_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked,
                                        !kIncludeCongestionFeedback);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientPriorityPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       quic::QuicStreamId id,
       quic::QuicStreamId parent_stream_id,
@@ -2306,7 +2304,7 @@
   quic::QuicStreamOffset header_stream_offset = 0;
   AddWrite(ConstructInitialSettingsPacket(&header_stream_offset));
 
-  quic::QuicPacketNumber client_packet_number = 2;
+  uint64_t client_packet_number = 2;
   if (client_headers_include_h2_stream_dependency_ &&
       version_ >= quic::QUIC_VERSION_43) {
     AddWrite(ConstructClientPriorityPacket(
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 379078a..e13c474 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -313,38 +313,38 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientConnectionClosePacket(quic::QuicPacketNumber num) {
+  ConstructClientConnectionClosePacket(uint64_t num) {
     return client_maker_.MakeConnectionClosePacket(
         num, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!");
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructServerConnectionClosePacket(quic::QuicPacketNumber num) {
+  ConstructServerConnectionClosePacket(uint64_t num) {
     return server_maker_.MakeConnectionClosePacket(
         num, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!");
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerGoAwayPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       quic::QuicErrorCode error_code,
       std::string reason_phrase) {
     return server_maker_.MakeGoAwayPacket(num, error_code, reason_phrase);
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked, true);
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicTime::Delta ack_delay_time) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked, true,
@@ -352,19 +352,19 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckAndRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckAndRstPacket(
         num, false, stream_id, error_code, largest_received, smallest_received,
         least_unacked, true);
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
       size_t bytes_written) {
@@ -373,22 +373,21 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientAckAndConnectionClosePacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+  ConstructClientAckAndConnectionClosePacket(uint64_t packet_number,
+                                             uint64_t largest_received,
+                                             uint64_t smallest_received,
+                                             uint64_t least_unacked) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked, true);
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
   ConstructClientAckAndConnectionClosePacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       quic::QuicTime::Delta delta_time_largest_observed,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicErrorCode quic_error,
       const std::string& quic_error_details) {
     return client_maker_.MakeAckAndConnectionClosePacket(
@@ -397,7 +396,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code) {
@@ -406,22 +405,22 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset* offset) {
     return client_maker_.MakeInitialSettingsPacket(packet_number, offset);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return server_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked, false);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructClientPriorityPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       quic::QuicStreamId id,
       quic::QuicStreamId parent_stream_id,
@@ -434,11 +433,11 @@
 
   std::unique_ptr<quic::QuicEncryptedPacket>
   ConstructClientAckAndPriorityFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       const std::vector<QuicTestPacketMaker::Http2StreamDependency>&
           priority_frames,
       quic::QuicStreamOffset* offset) {
@@ -477,7 +476,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -488,7 +487,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -500,7 +499,7 @@
 
   std::unique_ptr<quic::QuicEncryptedPacket>
   ConstructClientMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -512,12 +511,12 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckAndDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version,
       quic::QuicStreamId stream_id,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data) {
@@ -528,12 +527,12 @@
 
   std::unique_ptr<quic::QuicEncryptedPacket>
   ConstructClientAckAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version,
       quic::QuicStreamId stream_id,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       const std::vector<std::string> data_writes) {
@@ -543,7 +542,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientForceHolDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -554,7 +553,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       bool fin,
@@ -565,7 +564,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       bool fin,
@@ -577,7 +576,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       bool fin,
@@ -590,7 +589,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       bool fin,
@@ -607,7 +606,7 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructClientRequestHeadersAndDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -626,7 +625,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientMultipleDataFramesPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientMultipleDataFramesPacket(uint64_t packet_number,
                                           quic::QuicStreamId stream_id,
                                           bool should_include_version,
                                           bool fin,
@@ -637,7 +636,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerPushPromisePacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       quic::QuicStreamId promised_stream_id,
       bool should_include_version,
@@ -650,7 +649,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructServerResponseHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructServerResponseHeadersPacket(uint64_t packet_number,
                                        quic::QuicStreamId stream_id,
                                        bool should_include_version,
                                        bool fin,
@@ -661,7 +660,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructServerResponseHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructServerResponseHeadersPacket(uint64_t packet_number,
                                        quic::QuicStreamId stream_id,
                                        bool should_include_version,
                                        bool fin,
@@ -1244,7 +1243,7 @@
   spdy::SpdySerializedFrame spdy_frame =
       response_framer.SerializeFrame(headers_frame);
 
-  quic::QuicPacketNumber packet_number = 1;
+  uint64_t packet_number = 1;
   size_t chunk_size = 1200;
   for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
     size_t len = std::min(chunk_size, spdy_frame.size() - offset);
@@ -1303,7 +1302,7 @@
   spdy::SpdySerializedFrame spdy_frame =
       response_framer.SerializeFrame(headers_frame);
 
-  quic::QuicPacketNumber packet_number = 1;
+  uint64_t packet_number = 1;
   size_t chunk_size = 1200;
   for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
     size_t len = std::min(chunk_size, spdy_frame.size() - offset);
@@ -6379,7 +6378,7 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  quic::QuicPacketNumber client_packet_number = 1;
+  uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(client_packet_number++,
                                                   &header_stream_offset));
@@ -6470,7 +6469,7 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  quic::QuicPacketNumber client_packet_number = 1;
+  uint64_t client_packet_number = 1;
   // Initial SETTINGS frame.
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(client_packet_number++,
@@ -6725,7 +6724,7 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  quic::QuicPacketNumber client_packet_number = 1;
+  uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(client_packet_number++,
                                                   &header_stream_offset));
@@ -6983,7 +6982,7 @@
         expiration, supported_versions_);
   }
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       quic::QuicStreamOffset* offset,
@@ -6993,7 +6992,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       quic::QuicStreamId parent_stream_id,
@@ -7009,7 +7008,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientRequestHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructClientRequestHeadersPacket(uint64_t packet_number,
                                       quic::QuicStreamId stream_id,
                                       bool should_include_version,
                                       QuicTestPacketMaker* maker) {
@@ -7018,7 +7017,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructServerResponseHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructServerResponseHeadersPacket(uint64_t packet_number,
                                        quic::QuicStreamId stream_id,
                                        quic::QuicStreamOffset* offset,
                                        QuicTestPacketMaker* maker) {
@@ -7028,7 +7027,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructServerResponseHeadersPacket(quic::QuicPacketNumber packet_number,
+  ConstructServerResponseHeadersPacket(uint64_t packet_number,
                                        quic::QuicStreamId stream_id,
                                        QuicTestPacketMaker* maker) {
     return ConstructServerResponseHeadersPacket(packet_number, stream_id,
@@ -7036,7 +7035,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       QuicTestPacketMaker* maker) {
     quic::QuicString header = "";
@@ -7051,17 +7050,17 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       QuicTestPacketMaker* maker) {
     return maker->MakeAckPacket(packet_number, largest_received,
                                 smallest_received, least_unacked, true);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset* offset,
       QuicTestPacketMaker* maker) {
     return maker->MakeInitialSettingsPacket(packet_number, offset);
@@ -7406,7 +7405,7 @@
 
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
-  quic::QuicPacketNumber client_packet_number = 1;
+  uint64_t client_packet_number = 1;
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructInitialSettingsPacket(client_packet_number++,
                                                   &header_stream_offset));
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 8641a65..05fe8469 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -261,28 +261,28 @@
   // Helper functions for constructing packets sent by the client
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructSettingsPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return client_maker_.MakeInitialSettingsPacket(packet_number,
                                                    &header_stream_offset_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckAndRstPacket(
         packet_number, !kIncludeVersion, client_data_stream_id1_, error_code,
         largest_received, smallest_received, least_unacked, kSendFeedback);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       size_t bytes_written) {
     return client_maker_.MakeAckAndRstPacket(
         packet_number, !kIncludeVersion, client_data_stream_id1_, error_code,
@@ -291,7 +291,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code,
       size_t bytes_written) {
     return client_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
@@ -300,7 +300,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectRequestPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       RequestPriority request_priority = LOWEST) {
     spdy::SpdyHeaderBlock block;
     PopulateConnectRequestIR(&block);
@@ -311,7 +311,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectAuthRequestPacket(
-      quic::QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     spdy::SpdyHeaderBlock block;
     PopulateConnectRequestIR(&block);
     block["proxy-authorization"] = "Basic Zm9vOmJhcg==";
@@ -322,7 +322,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data) {
     return client_maker_.MakeDataPacket(packet_number, client_data_stream_id1_,
@@ -330,7 +330,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset offset,
       const std::vector<std::string> data_writes) {
     return client_maker_.MakeMultipleDataFramesPacket(
@@ -339,10 +339,10 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndDataPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data) {
     return client_maker_.MakeAckAndDataPacket(
@@ -353,10 +353,10 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   ConstructAckAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicStreamOffset offset,
       const std::vector<std::string> data_writes) {
     return client_maker_.MakeAckAndMultipleDataFramesPacket(
@@ -366,10 +366,10 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked) {
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked) {
     return client_maker_.MakeAckPacket(packet_number, largest_received,
                                        smallest_received, least_unacked,
                                        kSendFeedback);
@@ -378,7 +378,7 @@
   // Helper functions for constructing packets sent by the server
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code,
       size_t bytes_written) {
     return server_maker_.MakeRstPacket(packet_number, !kIncludeVersion,
@@ -387,7 +387,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data) {
     return server_maker_.MakeDataPacket(packet_number, client_data_stream_id1_,
@@ -395,7 +395,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerDataFinPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data) {
     return server_maker_.MakeDataPacket(packet_number, client_data_stream_id1_,
@@ -403,7 +403,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerConnectReplyPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool fin) {
     spdy::SpdyHeaderBlock block;
     block[":status"] = "200";
@@ -414,8 +414,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructServerConnectAuthReplyPacket(quic::QuicPacketNumber packet_number,
-                                        bool fin) {
+  ConstructServerConnectAuthReplyPacket(uint64_t packet_number, bool fin) {
     spdy::SpdyHeaderBlock block;
     block[":status"] = "407";
     block["proxy-authenticate"] = "Basic realm=\"MyRealm1\"";
@@ -425,9 +424,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructServerConnectRedirectReplyPacket(
-      quic::QuicPacketNumber packet_number,
-      bool fin) {
+  ConstructServerConnectRedirectReplyPacket(uint64_t packet_number, bool fin) {
     spdy::SpdyHeaderBlock block;
     block[":status"] = "302";
     block["location"] = kRedirectUrl;
@@ -438,8 +435,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket>
-  ConstructServerConnectErrorReplyPacket(quic::QuicPacketNumber packet_number,
-                                         bool fin) {
+  ConstructServerConnectErrorReplyPacket(uint64_t packet_number, bool fin) {
     spdy::SpdyHeaderBlock block;
     block[":status"] = "500";
 
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index b1aeb42e..72ca1eb7 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -421,13 +421,13 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket>
-  ConstructClientConnectionClosePacket(quic::QuicPacketNumber num) {
+  ConstructClientConnectionClosePacket(uint64_t num) {
     return client_maker_.MakeConnectionClosePacket(
         num, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!");
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicRstStreamErrorCode error_code) {
     quic::QuicStreamId stream_id =
         GetNthClientInitiatedBidirectionalStreamId(0);
@@ -453,7 +453,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin) {
@@ -468,7 +468,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       quic::QuicStreamId parent_stream_id,
       bool should_include_version,
@@ -485,7 +485,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -496,7 +496,7 @@
   }
 
   std::unique_ptr<quic::QuicEncryptedPacket> ConstructOkResponsePacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin) {
@@ -512,7 +512,7 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset* offset) {
     return client_maker_.MakeInitialSettingsPacket(packet_number, offset);
   }
@@ -7837,7 +7837,7 @@
   QuicStreamFactoryPeer::SetYieldAfterPackets(factory_.get(), 0);
 
   MockQuicData socket_data;
-  socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(0));
+  socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(1));
   socket_data.AddRead(ASYNC, OK);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
@@ -7886,7 +7886,7 @@
       factory_.get(), quic::QuicTime::Delta::FromMilliseconds(-1));
 
   MockQuicData socket_data;
-  socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(0));
+  socket_data.AddRead(SYNCHRONOUS, ConstructClientConnectionClosePacket(1));
   socket_data.AddRead(ASYNC, OK);
   socket_data.AddSocketDataToFactory(socket_factory_.get());
 
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 09c2c44..d0e5fcd8 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -18,9 +18,9 @@
 namespace test {
 namespace {
 
-quic::QuicAckFrame MakeAckFrame(quic::QuicPacketNumber largest_observed) {
+quic::QuicAckFrame MakeAckFrame(uint64_t largest_observed) {
   quic::QuicAckFrame ack;
-  ack.largest_acked = largest_observed;
+  ack.largest_acked = quic::QuicPacketNumber(largest_observed);
   return ack;
 }
 
@@ -56,7 +56,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeConnectivityProbingPacket(quic::QuicPacketNumber num,
+QuicTestPacketMaker::MakeConnectivityProbingPacket(uint64_t num,
                                                    bool include_version) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
@@ -67,7 +67,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicFramer framer(quic::test::SupportedVersions(quic::ParsedQuicVersion(
                               quic::PROTOCOL_QUIC_CRYPTO, version_)),
@@ -103,7 +103,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakePingPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
@@ -114,14 +114,14 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicPingFrame ping;
   return MakePacket(header, quic::QuicFrame(ping));
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeDummyCHLOPacket(quic::QuicPacketNumber packet_num) {
+QuicTestPacketMaker::MakeDummyCHLOPacket(uint64_t packet_num) {
   encryption_level_ = quic::ENCRYPTION_NONE;
   SetLongHeaderType(quic::INITIAL);
   InitializeHeader(packet_num, /*include_version=*/true);
@@ -144,12 +144,11 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeAckAndPingPacket(
-    quic::QuicPacketNumber num,
-    bool include_version,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked) {
+QuicTestPacketMaker::MakeAckAndPingPacket(uint64_t num,
+                                          bool include_version,
+                                          uint64_t largest_received,
+                                          uint64_t smallest_received,
+                                          uint64_t least_unacked) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.destination_connection_id_length = GetDestinationConnectionIdLength();
@@ -159,16 +158,17 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = quic::QuicTime::Delta::Zero();
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -176,7 +176,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -188,7 +188,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeRstPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId stream_id,
     quic::QuicRstStreamErrorCode error_code) {
@@ -196,7 +196,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeRstPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId stream_id,
     quic::QuicRstStreamErrorCode error_code,
@@ -210,7 +210,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicFrames frames;
 
@@ -229,7 +229,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeStreamIdBlockedPacket(quic::QuicPacketNumber num,
+QuicTestPacketMaker::MakeStreamIdBlockedPacket(uint64_t num,
                                                bool include_version,
                                                quic::QuicStreamId stream_id) {
   quic::QuicPacketHeader header;
@@ -241,7 +241,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicStreamIdBlockedFrame frame(1, stream_id);
   DVLOG(1) << "Adding frame: " << quic::QuicFrame(frame);
@@ -249,7 +249,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeMaxStreamIdPacket(quic::QuicPacketNumber num,
+QuicTestPacketMaker::MakeMaxStreamIdPacket(uint64_t num,
                                            bool include_version,
                                            quic::QuicStreamId stream_id) {
   quic::QuicPacketHeader header;
@@ -261,7 +261,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicMaxStreamIdFrame frame(1, stream_id);
   DVLOG(1) << "Adding frame: " << quic::QuicFrame(frame);
@@ -270,7 +270,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRstAndRequestHeadersPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId rst_stream_id,
     quic::QuicRstStreamErrorCode rst_error_code,
@@ -317,13 +317,13 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeAckAndRstPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId stream_id,
     quic::QuicRstStreamErrorCode error_code,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback) {
   return MakeAckAndRstPacket(num, include_version, stream_id, error_code,
                              largest_received, smallest_received, least_unacked,
@@ -332,13 +332,13 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeAckAndRstPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId stream_id,
     quic::QuicRstStreamErrorCode error_code,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback,
     size_t bytes_written) {
   quic::QuicPacketHeader header;
@@ -350,16 +350,17 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = quic::QuicTime::Delta::Zero();
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -367,7 +368,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -389,14 +390,14 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRstAckAndConnectionClosePacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicStreamId stream_id,
     quic::QuicRstStreamErrorCode error_code,
     quic::QuicTime::Delta ack_delay_time,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     quic::QuicErrorCode quic_error,
     const std::string& quic_error_details) {
   quic::QuicPacketHeader header;
@@ -408,7 +409,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicFrames frames;
   quic::QuicRstStreamFrame rst(1, stream_id, error_code, 0);
@@ -426,19 +427,20 @@
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = ack_delay_time;
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   frames.push_back(quic::QuicFrame(&ack));
   DVLOG(1) << "Adding frame: " << frames.back();
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -455,12 +457,12 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeAckAndConnectionClosePacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicTime::Delta ack_delay_time,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     quic::QuicErrorCode quic_error,
     const std::string& quic_error_details) {
   quic::QuicPacketHeader header;
@@ -472,16 +474,17 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = ack_delay_time;
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -489,7 +492,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -506,7 +509,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeConnectionClosePacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     bool include_version,
     quic::QuicErrorCode quic_error,
     const std::string& quic_error_details) {
@@ -519,7 +522,7 @@
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicConnectionCloseFrame close;
   close.error_code = quic_error;
@@ -528,7 +531,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeGoAwayPacket(
-    quic::QuicPacketNumber num,
+    uint64_t num,
     quic::QuicErrorCode error_code,
     std::string reason_phrase) {
   quic::QuicPacketHeader header;
@@ -540,7 +543,7 @@
   header.version_flag = ShouldIncludeVersion(false);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = num;
+  header.packet_number = quic::QuicPacketNumber(num);
 
   quic::QuicGoAwayFrame goaway;
   goaway.error_code = error_code;
@@ -550,10 +553,10 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t packet_number,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback) {
   return MakeAckPacket(packet_number, 1, largest_received, smallest_received,
                        least_unacked, send_feedback,
@@ -561,11 +564,11 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicPacketNumber first_received,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t packet_number,
+    uint64_t first_received,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback) {
   return MakeAckPacket(packet_number, first_received, largest_received,
                        smallest_received, least_unacked, send_feedback,
@@ -573,10 +576,10 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t packet_number,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback,
     quic::QuicTime::Delta ack_delay_time) {
   return MakeAckPacket(packet_number, 1, largest_received, smallest_received,
@@ -584,11 +587,11 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicPacketNumber first_received,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t packet_number,
+    uint64_t first_received,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool send_feedback,
     quic::QuicTime::Delta ack_delay_time) {
   quic::QuicPacketHeader header;
@@ -600,17 +603,18 @@
   header.version_flag = ShouldIncludeVersion(false);
   header.long_packet_type = long_header_type_;
   header.packet_number_length = GetPacketNumberLength();
-  header.packet_number = packet_number;
+  header.packet_number = quic::QuicPacketNumber(packet_number);
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = ack_delay_time;
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
     DCHECK_GE(largest_received, first_received);
-    ack.packets.AddRange(first_received, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(first_received),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFramer framer(quic::test::SupportedVersions(quic::ParsedQuicVersion(
                               quic::PROTOCOL_QUIC_CRYPTO, version_)),
@@ -622,7 +626,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -641,7 +645,7 @@
 
 // Returns a newly created packet to send kData on stream 1.
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeDataPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -655,7 +659,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeMultipleDataFramesPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -675,26 +679,26 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeAckAndDataPacket(
-    quic::QuicPacketNumber packet_number,
-    bool include_version,
-    quic::QuicStreamId stream_id,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
-    bool fin,
-    quic::QuicStreamOffset offset,
-    quic::QuicStringPiece data) {
+QuicTestPacketMaker::MakeAckAndDataPacket(uint64_t packet_number,
+                                          bool include_version,
+                                          quic::QuicStreamId stream_id,
+                                          uint64_t largest_received,
+                                          uint64_t smallest_received,
+                                          uint64_t least_unacked,
+                                          bool fin,
+                                          quic::QuicStreamOffset offset,
+                                          quic::QuicStringPiece data) {
   InitializeHeader(packet_number, include_version);
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = quic::QuicTime::Delta::Zero();
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -702,7 +706,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -716,12 +720,12 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeAckAndMultipleDataFramesPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     bool include_version,
     quic::QuicStreamId stream_id,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     bool fin,
     quic::QuicStreamOffset offset,
     const std::vector<std::string>& data_writes) {
@@ -729,12 +733,13 @@
 
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = quic::QuicTime::Delta::Zero();
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -742,7 +747,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
@@ -760,7 +765,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersAndMultipleDataFramesPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -807,7 +812,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -824,7 +829,7 @@
 // Will also update the value after packet creation.
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -842,7 +847,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersPacketAndSaveData(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -877,7 +882,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersAndRstPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -948,7 +953,7 @@
 // |spdy_headers_frame_length|.
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeRequestHeadersPacketWithOffsetTracking(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -965,7 +970,7 @@
 // Will also update the value after packet creation.
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakePushPromisePacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     quic::QuicStreamId promised_stream_id,
     bool should_include_version,
@@ -997,13 +1002,12 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeForceHolDataPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicStreamId stream_id,
-    bool should_include_version,
-    bool fin,
-    quic::QuicStreamOffset* offset,
-    quic::QuicStringPiece data) {
+QuicTestPacketMaker::MakeForceHolDataPacket(uint64_t packet_number,
+                                            quic::QuicStreamId stream_id,
+                                            bool should_include_version,
+                                            bool fin,
+                                            quic::QuicStreamOffset* offset,
+                                            quic::QuicStringPiece data) {
   spdy::SpdyDataIR spdy_data(stream_id, data);
   spdy_data.set_fin(fin);
   spdy::SpdySerializedFrame spdy_frame(
@@ -1020,7 +1024,7 @@
 // Will also update the value after packet creation.
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeResponseHeadersPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -1052,7 +1056,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeResponseHeadersPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -1067,7 +1071,7 @@
 // |spdy_headers_frame_length|.
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeResponseHeadersPacketWithOffsetTracking(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamId stream_id,
     bool should_include_version,
     bool fin,
@@ -1145,7 +1149,7 @@
   return encrypted.Clone();
 }
 
-void QuicTestPacketMaker::InitializeHeader(quic::QuicPacketNumber packet_number,
+void QuicTestPacketMaker::InitializeHeader(uint64_t packet_number,
                                            bool should_include_version) {
   header_.destination_connection_id = connection_id_;
   header_.destination_connection_id_length = GetDestinationConnectionIdLength();
@@ -1155,13 +1159,12 @@
   header_.version_flag = ShouldIncludeVersion(should_include_version);
   header_.long_packet_type = long_header_type_;
   header_.packet_number_length = GetPacketNumberLength();
-  header_.packet_number = packet_number;
+  header_.packet_number = quic::QuicPacketNumber(packet_number);
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakeInitialSettingsPacket(
-    quic::QuicPacketNumber packet_number,
-    quic::QuicStreamOffset* offset) {
+QuicTestPacketMaker::MakeInitialSettingsPacket(uint64_t packet_number,
+                                               quic::QuicStreamOffset* offset) {
   std::string unused_data;
   return MakeInitialSettingsPacketAndSaveData(packet_number, offset,
                                               &unused_data);
@@ -1169,7 +1172,7 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeInitialSettingsPacketAndSaveData(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     quic::QuicStreamOffset* offset,
     std::string* stream_data) {
   spdy::SpdySettingsIR settings_frame;
@@ -1193,7 +1196,7 @@
 }
 
 std::unique_ptr<quic::QuicReceivedPacket>
-QuicTestPacketMaker::MakePriorityPacket(quic::QuicPacketNumber packet_number,
+QuicTestPacketMaker::MakePriorityPacket(uint64_t packet_number,
                                         bool should_include_version,
                                         quic::QuicStreamId id,
                                         quic::QuicStreamId parent_stream_id,
@@ -1223,21 +1226,22 @@
 
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeAckAndMultiplePriorityFramesPacket(
-    quic::QuicPacketNumber packet_number,
+    uint64_t packet_number,
     bool should_include_version,
-    quic::QuicPacketNumber largest_received,
-    quic::QuicPacketNumber smallest_received,
-    quic::QuicPacketNumber least_unacked,
+    uint64_t largest_received,
+    uint64_t smallest_received,
+    uint64_t least_unacked,
     const std::vector<Http2StreamDependency>& priority_frames,
     quic::QuicStreamOffset* offset) {
   quic::QuicAckFrame ack(MakeAckFrame(largest_received));
   ack.ack_delay_time = quic::QuicTime::Delta::Zero();
-  for (quic::QuicPacketNumber i = smallest_received; i <= largest_received;
-       ++i) {
-    ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
+  for (uint64_t i = smallest_received; i <= largest_received; ++i) {
+    ack.received_packet_times.push_back(
+        std::make_pair(quic::QuicPacketNumber(i), clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    ack.packets.AddRange(quic::QuicPacketNumber(1),
+                         quic::QuicPacketNumber(largest_received + 1));
   }
   quic::QuicFrames frames;
   frames.push_back(quic::QuicFrame(&ack));
@@ -1245,7 +1249,7 @@
 
   quic::QuicStopWaitingFrame stop_waiting;
   if (version_ == quic::QUIC_VERSION_35) {
-    stop_waiting.least_unacked = least_unacked;
+    stop_waiting.least_unacked = quic::QuicPacketNumber(least_unacked);
     frames.push_back(quic::QuicFrame(&stop_waiting));
     DVLOG(1) << "Adding frame: " << frames.back();
   }
diff --git a/net/quic/quic_test_packet_maker.h b/net/quic/quic_test_packet_maker.h
index 33b8d365..0504966 100644
--- a/net/quic/quic_test_packet_maker.h
+++ b/net/quic/quic_test_packet_maker.h
@@ -49,45 +49,45 @@
 
   void set_hostname(const std::string& host);
   std::unique_ptr<quic::QuicReceivedPacket> MakeConnectivityProbingPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version);
   std::unique_ptr<quic::QuicReceivedPacket> MakePingPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version);
   std::unique_ptr<quic::QuicReceivedPacket> MakeDummyCHLOPacket(
-      quic::QuicPacketNumber packet_num);
+      uint64_t packet_num);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndPingPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked);
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeStreamIdBlockedPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeMaxStreamIdPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
       size_t bytes_written);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeRstAndRequestHeadersPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId rst_stream_id,
       quic::QuicRstStreamErrorCode rst_error_code,
@@ -100,126 +100,126 @@
       quic::QuicStreamOffset* offset);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndRstPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback,
       size_t bytes_written);
   std::unique_ptr<quic::QuicReceivedPacket> MakeRstAckAndConnectionClosePacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicStreamId stream_id,
       quic::QuicRstStreamErrorCode error_code,
       quic::QuicTime::Delta delta_time_largest_observed,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicErrorCode quic_error,
       const std::string& quic_error_details);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndConnectionClosePacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicTime::Delta delta_time_largest_observed,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       quic::QuicErrorCode quic_error,
       const std::string& quic_error_details);
   std::unique_ptr<quic::QuicReceivedPacket> MakeConnectionClosePacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       bool include_version,
       quic::QuicErrorCode quic_error,
       const std::string& quic_error_details);
   std::unique_ptr<quic::QuicReceivedPacket> MakeGoAwayPacket(
-      quic::QuicPacketNumber num,
+      uint64_t num,
       quic::QuicErrorCode error_code,
       std::string reason_phrase);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber first_received,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t first_received,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback,
       quic::QuicTime::Delta ack_delay_time);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicPacketNumber first_received,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t packet_number,
+      uint64_t first_received,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool send_feedback,
       quic::QuicTime::Delta ack_delay_time);
   std::unique_ptr<quic::QuicReceivedPacket> MakeDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data);
   std::unique_ptr<quic::QuicReceivedPacket> MakeForceHolDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset* offset,
       quic::QuicStringPiece data);
   std::unique_ptr<quic::QuicReceivedPacket> MakeMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
       quic::QuicStreamOffset offset,
       const std::vector<std::string>& data_writes);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndDataPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version,
       quic::QuicStreamId stream_id,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       quic::QuicStringPiece data);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool include_version,
       quic::QuicStreamId stream_id,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       bool fin,
       quic::QuicStreamOffset offset,
       const std::vector<std::string>& data);
 
   std::unique_ptr<quic::QuicReceivedPacket>
   MakeRequestHeadersAndMultipleDataFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -233,7 +233,7 @@
   // If |spdy_headers_frame_length| is non-null, it will be set to the size of
   // the SPDY headers frame created for this packet.
   std::unique_ptr<quic::QuicReceivedPacket> MakeRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -243,7 +243,7 @@
       size_t* spdy_headers_frame_length);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeRequestHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -255,7 +255,7 @@
 
   // Saves the serialized QUIC stream data in |stream_data|.
   std::unique_ptr<quic::QuicReceivedPacket> MakeRequestHeadersPacketAndSaveData(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -267,7 +267,7 @@
       std::string* stream_data);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeRequestHeadersAndRstPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -283,7 +283,7 @@
   // |spdy_headers_frame_length|.
   std::unique_ptr<quic::QuicReceivedPacket>
   MakeRequestHeadersPacketWithOffsetTracking(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -295,7 +295,7 @@
   // If |spdy_headers_frame_length| is non-null, it will be set to the size of
   // the SPDY headers frame created for this packet.
   std::unique_ptr<quic::QuicReceivedPacket> MakePushPromisePacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       quic::QuicStreamId promised_stream_id,
       bool should_include_version,
@@ -307,7 +307,7 @@
   // If |spdy_headers_frame_length| is non-null, it will be set to the size of
   // the SPDY headers frame created for this packet.
   std::unique_ptr<quic::QuicReceivedPacket> MakeResponseHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -316,7 +316,7 @@
       quic::QuicStreamOffset* offset);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakeResponseHeadersPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamId stream_id,
       bool should_include_version,
       bool fin,
@@ -326,29 +326,28 @@
   // Convenience method for calling MakeResponseHeadersPacket with nullptr for
   // |spdy_headers_frame_length|.
   std::unique_ptr<quic::QuicReceivedPacket>
-  MakeResponseHeadersPacketWithOffsetTracking(
-      quic::QuicPacketNumber packet_number,
-      quic::QuicStreamId stream_id,
-      bool should_include_version,
-      bool fin,
-      spdy::SpdyHeaderBlock headers,
-      quic::QuicStreamOffset* offset);
+  MakeResponseHeadersPacketWithOffsetTracking(uint64_t packet_number,
+                                              quic::QuicStreamId stream_id,
+                                              bool should_include_version,
+                                              bool fin,
+                                              spdy::SpdyHeaderBlock headers,
+                                              quic::QuicStreamOffset* offset);
 
   // Creates a packet containing the initial SETTINGS frame, and saves the
   // headers stream offset into |offset|.
   std::unique_ptr<quic::QuicReceivedPacket> MakeInitialSettingsPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       quic::QuicStreamOffset* offset);
 
   // Same as above, but also saves the serialized QUIC stream data in
   // |stream_data|.
   std::unique_ptr<quic::QuicReceivedPacket>
-  MakeInitialSettingsPacketAndSaveData(quic::QuicPacketNumber packet_number,
+  MakeInitialSettingsPacketAndSaveData(uint64_t packet_number,
                                        quic::QuicStreamOffset* offset,
                                        std::string* stream_data);
 
   std::unique_ptr<quic::QuicReceivedPacket> MakePriorityPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
       quic::QuicStreamId id,
       quic::QuicStreamId parent_stream_id,
@@ -357,11 +356,11 @@
 
   std::unique_ptr<quic::QuicReceivedPacket>
   MakeAckAndMultiplePriorityFramesPacket(
-      quic::QuicPacketNumber packet_number,
+      uint64_t packet_number,
       bool should_include_version,
-      quic::QuicPacketNumber largest_received,
-      quic::QuicPacketNumber smallest_received,
-      quic::QuicPacketNumber least_unacked,
+      uint64_t largest_received,
+      uint64_t smallest_received,
+      uint64_t least_unacked,
       const std::vector<Http2StreamDependency>& priority_frames,
       quic::QuicStreamOffset* offset);
 
@@ -391,8 +390,7 @@
       const quic::QuicPacketHeader& header,
       const quic::QuicFrames& frames);
 
-  void InitializeHeader(quic::QuicPacketNumber packet_number,
-                        bool should_include_version);
+  void InitializeHeader(uint64_t packet_number, bool should_include_version);
 
   spdy::SpdySerializedFrame MakeSpdyHeadersFrame(
       quic::QuicStreamId stream_id,
diff --git a/net/third_party/quic/core/chlo_extractor_test.cc b/net/third_party/quic/core/chlo_extractor_test.cc
index fbd1b96..fbb9c55 100644
--- a/net/third_party/quic/core/chlo_extractor_test.cc
+++ b/net/third_party/quic/core/chlo_extractor_test.cc
@@ -52,7 +52,7 @@
     header_.version = AllSupportedVersions().front();
     header_.reset_flag = false;
     header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER;
-    header_.packet_number = 1;
+    header_.packet_number = QuicPacketNumber(1);
   }
 
   void MakePacket(const QuicStreamFrame& stream_frame) {
diff --git a/net/third_party/quic/core/congestion_control/bandwidth_sampler.cc b/net/third_party/quic/core/congestion_control/bandwidth_sampler.cc
index 461b227..5d482cdf 100644
--- a/net/third_party/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/net/third_party/quic/core/congestion_control/bandwidth_sampler.cc
@@ -18,9 +18,7 @@
       total_bytes_sent_at_last_acked_packet_(0),
       last_acked_packet_sent_time_(QuicTime::Zero()),
       last_acked_packet_ack_time_(QuicTime::Zero()),
-      last_sent_packet_(0),
       is_app_limited_(false),
-      end_of_app_limited_phase_(0),
       connection_state_map_() {}
 
 BandwidthSampler::~BandwidthSampler() {}
diff --git a/net/third_party/quic/core/congestion_control/bandwidth_sampler_test.cc b/net/third_party/quic/core/congestion_control/bandwidth_sampler_test.cc
index 56c9795f..19e6f7d 100644
--- a/net/third_party/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/net/third_party/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -40,54 +40,55 @@
   BandwidthSampler sampler_;
   QuicByteCount bytes_in_flight_;
 
-  void SendPacketInner(QuicPacketNumber packet_number,
+  void SendPacketInner(uint64_t packet_number,
                        QuicByteCount bytes,
                        HasRetransmittableData has_retransmittable_data) {
-    sampler_.OnPacketSent(clock_.Now(), packet_number, bytes, bytes_in_flight_,
-                          has_retransmittable_data);
+    sampler_.OnPacketSent(clock_.Now(), QuicPacketNumber(packet_number), bytes,
+                          bytes_in_flight_, has_retransmittable_data);
     if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) {
       bytes_in_flight_ += bytes;
     }
   }
 
-  void SendPacket(QuicPacketNumber packet_number) {
+  void SendPacket(uint64_t packet_number) {
     SendPacketInner(packet_number, kRegularPacketSize,
                     HAS_RETRANSMITTABLE_DATA);
   }
 
-  BandwidthSample AckPacketInner(QuicPacketNumber packet_number) {
-    QuicByteCount size =
-        BandwidthSamplerPeer::GetPacketSize(sampler_, packet_number);
+  BandwidthSample AckPacketInner(uint64_t packet_number) {
+    QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
+        sampler_, QuicPacketNumber(packet_number));
     bytes_in_flight_ -= size;
-    return sampler_.OnPacketAcknowledged(clock_.Now(), packet_number);
+    return sampler_.OnPacketAcknowledged(clock_.Now(),
+                                         QuicPacketNumber(packet_number));
   }
 
   // Acknowledge receipt of a packet and expect it to be not app-limited.
-  QuicBandwidth AckPacket(QuicPacketNumber packet_number) {
+  QuicBandwidth AckPacket(uint64_t packet_number) {
     BandwidthSample sample = AckPacketInner(packet_number);
     EXPECT_FALSE(sample.is_app_limited);
     return sample.bandwidth;
   }
 
-  void LosePacket(QuicPacketNumber packet_number) {
-    QuicByteCount size =
-        BandwidthSamplerPeer::GetPacketSize(sampler_, packet_number);
+  void LosePacket(uint64_t packet_number) {
+    QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
+        sampler_, QuicPacketNumber(packet_number));
     bytes_in_flight_ -= size;
-    sampler_.OnPacketLost(packet_number);
+    sampler_.OnPacketLost(QuicPacketNumber(packet_number));
   }
 
   // Sends one packet and acks it.  Then, send 20 packets.  Finally, send
   // another 20 packets while acknowledging previous 20.
   void Send40PacketsAndAckFirst20(QuicTime::Delta time_between_packets) {
     // Send 20 packets at a constant inter-packet time.
-    for (QuicPacketNumber i = 1; i <= 20; i++) {
+    for (int i = 1; i <= 20; i++) {
       SendPacket(i);
       clock_.AdvanceTime(time_between_packets);
     }
 
     // Ack packets 1 to 20, while sending new packets at the same rate as
     // before.
-    for (QuicPacketNumber i = 1; i <= 20; i++) {
+    for (int i = 1; i <= 20; i++) {
       AckPacket(i);
       SendPacket(i + 20);
       clock_.AdvanceTime(time_between_packets);
@@ -102,7 +103,7 @@
       QuicBandwidth::FromBytesPerSecond(kRegularPacketSize * 100);
 
   // Send packets at the constant bandwidth.
-  for (QuicPacketNumber i = 1; i < 20; i++) {
+  for (int i = 1; i < 20; i++) {
     SendPacket(i);
     clock_.AdvanceTime(time_between_packets);
     QuicBandwidth current_sample = AckPacket(i);
@@ -110,7 +111,7 @@
   }
 
   // Send packets at the exponentially decreasing bandwidth.
-  for (QuicPacketNumber i = 20; i < 25; i++) {
+  for (int i = 20; i < 25; i++) {
     time_between_packets = time_between_packets * 2;
     expected_bandwidth = expected_bandwidth * 0.5;
 
@@ -135,7 +136,7 @@
 
   // Ack the packets 21 to 40, arriving at the correct bandwidth.
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
-  for (QuicPacketNumber i = 21; i <= 40; i++) {
+  for (int i = 21; i <= 40; i++) {
     last_bandwidth = AckPacket(i);
     EXPECT_EQ(expected_bandwidth, last_bandwidth);
     clock_.AdvanceTime(time_between_packets);
@@ -152,14 +153,14 @@
       QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize) * 0.5;
 
   // Send 20 packets, each 1 ms apart.
-  for (QuicPacketNumber i = 1; i <= 20; i++) {
+  for (int i = 1; i <= 20; i++) {
     SendPacket(i);
     clock_.AdvanceTime(time_between_packets);
   }
 
   // Ack packets 1 to 20, losing every even-numbered packet, while sending new
   // packets at the same rate as before.
-  for (QuicPacketNumber i = 1; i <= 20; i++) {
+  for (int i = 1; i <= 20; i++) {
     if (i % 2 == 0) {
       AckPacket(i);
     } else {
@@ -171,7 +172,7 @@
 
   // Ack the packets 21 to 40 with the same loss pattern.
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
-  for (QuicPacketNumber i = 21; i <= 40; i++) {
+  for (int i = 21; i <= 40; i++) {
     if (i % 2 == 0) {
       last_bandwidth = AckPacket(i);
       EXPECT_EQ(expected_bandwidth, last_bandwidth);
@@ -196,7 +197,7 @@
 
   // Send 20 packets, each 1 ms apart. Every even packet is not congestion
   // controlled.
-  for (QuicPacketNumber i = 1; i <= 20; i++) {
+  for (int i = 1; i <= 20; i++) {
     SendPacketInner(
         i, kRegularPacketSize,
         i % 2 == 0 ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA);
@@ -208,7 +209,7 @@
 
   // Ack packets 2 to 21, ignoring every even-numbered packet, while sending new
   // packets at the same rate as before.
-  for (QuicPacketNumber i = 1; i <= 20; i++) {
+  for (int i = 1; i <= 20; i++) {
     if (i % 2 == 0) {
       AckPacket(i);
     }
@@ -220,7 +221,7 @@
 
   // Ack the packets 22 to 41 with the same congestion controlled pattern.
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
-  for (QuicPacketNumber i = 21; i <= 40; i++) {
+  for (int i = 21; i <= 40; i++) {
     if (i % 2 == 0) {
       last_bandwidth = AckPacket(i);
       EXPECT_EQ(expected_bandwidth, last_bandwidth);
@@ -251,7 +252,7 @@
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
   QuicTime::Delta ridiculously_small_time_delta =
       QuicTime::Delta::FromMicroseconds(20);
-  for (QuicPacketNumber i = 21; i <= 40; i++) {
+  for (int i = 21; i <= 40; i++) {
     last_bandwidth = AckPacket(i);
     clock_.AdvanceTime(ridiculously_small_time_delta);
   }
@@ -272,7 +273,7 @@
   // Ack the packets 21 to 40 in the reverse order, while sending packets 41 to
   // 60.
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
-  for (QuicPacketNumber i = 0; i < 20; i++) {
+  for (int i = 0; i < 20; i++) {
     last_bandwidth = AckPacket(40 - i);
     EXPECT_EQ(expected_bandwidth, last_bandwidth);
     SendPacket(41 + i);
@@ -280,7 +281,7 @@
   }
 
   // Ack the packets 41 to 60, now in the regular order.
-  for (QuicPacketNumber i = 41; i <= 60; i++) {
+  for (int i = 41; i <= 60; i++) {
     last_bandwidth = AckPacket(i);
     EXPECT_EQ(expected_bandwidth, last_bandwidth);
     clock_.AdvanceTime(time_between_packets);
@@ -301,7 +302,7 @@
   // We are now app-limited. Ack 21 to 40 as usual, but do not send anything for
   // now.
   sampler_.OnAppLimited();
-  for (QuicPacketNumber i = 21; i <= 40; i++) {
+  for (int i = 21; i <= 40; i++) {
     QuicBandwidth current_sample = AckPacket(i);
     EXPECT_EQ(expected_bandwidth, current_sample);
     clock_.AdvanceTime(time_between_packets);
@@ -311,14 +312,14 @@
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
 
   // Send packets 41 to 60, all of which would be marked as app-limited.
-  for (QuicPacketNumber i = 41; i <= 60; i++) {
+  for (int i = 41; i <= 60; i++) {
     SendPacket(i);
     clock_.AdvanceTime(time_between_packets);
   }
 
   // Ack packets 41 to 60, while sending packets 61 to 80.  41 to 60 should be
   // app-limited and underestimate the bandwidth due to that.
-  for (QuicPacketNumber i = 41; i <= 60; i++) {
+  for (int i = 41; i <= 60; i++) {
     BandwidthSample sample = AckPacketInner(i);
     EXPECT_TRUE(sample.is_app_limited);
     EXPECT_LT(sample.bandwidth, 0.7f * expected_bandwidth);
@@ -329,7 +330,7 @@
 
   // Run out of packets, and then ack packet 61 to 80, all of which should have
   // correct non-app-limited samples.
-  for (QuicPacketNumber i = 61; i <= 80; i++) {
+  for (int i = 61; i <= 80; i++) {
     QuicBandwidth last_bandwidth = AckPacket(i);
     EXPECT_EQ(expected_bandwidth, last_bandwidth);
     clock_.AdvanceTime(time_between_packets);
@@ -349,7 +350,7 @@
   const QuicBandwidth real_bandwidth =
       QuicBandwidth::FromBytesAndTimeDelta(num_bytes, rtt);
 
-  for (QuicPacketNumber i = 1; i <= 10; i++) {
+  for (int i = 1; i <= 10; i++) {
     SendPacket(i);
     clock_.AdvanceTime(time_between_packets);
   }
@@ -357,7 +358,7 @@
   clock_.AdvanceTime(rtt - num_packets * time_between_packets);
 
   QuicBandwidth last_sample = QuicBandwidth::Zero();
-  for (QuicPacketNumber i = 1; i <= 10; i++) {
+  for (int i = 1; i <= 10; i++) {
     QuicBandwidth sample = AckPacket(i);
     EXPECT_GT(sample, last_sample);
     last_sample = sample;
@@ -385,9 +386,9 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
 
   EXPECT_EQ(5u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
-  sampler_.RemoveObsoletePackets(4);
+  sampler_.RemoveObsoletePackets(QuicPacketNumber(4));
   EXPECT_EQ(2u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
-  sampler_.OnPacketLost(4);
+  sampler_.OnPacketLost(QuicPacketNumber(4));
   EXPECT_EQ(1u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
   AckPacket(5);
   EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender.cc b/net/third_party/quic/core/congestion_control/bbr_sender.cc
index cb736e2..1d00ce68 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender.cc
@@ -87,8 +87,6 @@
       random_(random),
       mode_(STARTUP),
       round_trip_count_(0),
-      last_sent_packet_(0),
-      current_round_trip_end_(0),
       max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
       max_ack_height_(kBandwidthWindowSize, 0, 0),
       aggregation_epoch_start_time_(QuicTime::Zero()),
@@ -122,7 +120,6 @@
       has_non_app_limited_sample_(false),
       flexible_app_limited_(false),
       recovery_state_(NOT_IN_RECOVERY),
-      end_recovery_at_(0),
       recovery_window_(max_congestion_window_),
       is_app_limited_recovery_(false),
       slower_startup_(false),
@@ -460,7 +457,8 @@
 }
 
 bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) {
-  if (last_acked_packet > current_round_trip_end_) {
+  if (!current_round_trip_end_.IsInitialized() ||
+      last_acked_packet > current_round_trip_end_) {
     round_trip_count_++;
     current_round_trip_end_ = last_sent_packet_;
     return true;
@@ -748,7 +746,7 @@
     return;
   }
   // Slow the pacing rate in STARTUP once loss has ever been detected.
-  const bool has_ever_detected_loss = end_recovery_at_ > 0;
+  const bool has_ever_detected_loss = end_recovery_at_.IsInitialized();
   if (slower_startup_ && has_ever_detected_loss &&
       has_non_app_limited_sample_) {
     pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate();
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
index 85c1c01..313725d 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
@@ -1094,7 +1094,7 @@
   QuicBandwidth pacing_rate = original_pacing_rate;
   const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(0, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(), kMaxPacketSize));
   QuicPacketNumber largest_sent =
       bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
   for (QuicPacketNumber packet_number =
@@ -1144,7 +1144,7 @@
   QuicBandwidth pacing_rate = original_pacing_rate;
   const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(0, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(), kMaxPacketSize));
   QuicPacketNumber largest_sent =
       bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
   for (QuicPacketNumber packet_number =
diff --git a/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc b/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc
index 8420f3d9..9af4179 100644
--- a/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/net/third_party/quic/core/congestion_control/general_loss_algorithm.cc
@@ -31,7 +31,6 @@
 
 GeneralLossAlgorithm::GeneralLossAlgorithm(LossDetectionType loss_type)
     : loss_detection_timeout_(QuicTime::Zero()),
-      largest_lost_(0),
       least_in_flight_(1),
       faster_detect_loss_(GetQuicReloadableFlag(quic_faster_detect_loss)) {
   SetLossDetectionType(loss_type);
@@ -39,7 +38,7 @@
 
 void GeneralLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) {
   loss_detection_timeout_ = QuicTime::Zero();
-  largest_sent_on_spurious_retransmit_ = kInvalidPacketNumber;
+  largest_sent_on_spurious_retransmit_.Clear();
   loss_type_ = loss_type;
   reordering_shift_ = loss_type == kAdaptiveTime
                           ? kDefaultAdaptiveLossDelayShift
@@ -49,7 +48,7 @@
     QUIC_RELOADABLE_FLAG_COUNT(quic_eighth_rtt_loss_detection);
     reordering_shift_ = 3;
   }
-  largest_previously_acked_ = kInvalidPacketNumber;
+  largest_previously_acked_.Clear();
 }
 
 LossDetectionType GeneralLossAlgorithm::GetLossDetectionType() const {
@@ -91,7 +90,7 @@
   QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked();
   auto it = unacked_packets.begin();
   if (faster_detect_loss_) {
-    if (least_in_flight_ >= packet_number) {
+    if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) {
       if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) {
         QUIC_BUG << "least_in_flight: " << least_in_flight_
                  << " is greater than largest_sent_packet + 1: "
@@ -103,9 +102,9 @@
       }
     }
     // Clear least_in_flight_.
-    least_in_flight_ = kInvalidPacketNumber;
+    least_in_flight_.Clear();
   } else {
-    if (largest_lost_ >= packet_number) {
+    if (largest_lost_.IsInitialized() && largest_lost_ >= packet_number) {
       if (largest_lost_ > unacked_packets.largest_sent_packet()) {
         QUIC_BUG << "largest_lost: " << largest_lost_
                  << " is greater than largest_sent_packet: "
@@ -132,7 +131,8 @@
     } else if (loss_type_ == kLazyFack) {
       // Require two in order acks to invoke FACK, which avoids spuriously
       // retransmitting packets when one packet is reordered by a large amount.
-      if (largest_newly_acked > largest_previously_acked_ &&
+      if (largest_previously_acked_.IsInitialized() &&
+          largest_newly_acked > largest_previously_acked_ &&
           largest_previously_acked_ > packet_number &&
           largest_previously_acked_ - packet_number >=
               (kNumberOfNacksBeforeRetransmission - 1)) {
@@ -150,7 +150,7 @@
       QuicTime when_lost = it->sent_time + loss_delay;
       if (time < when_lost) {
         loss_detection_timeout_ = when_lost;
-        if (least_in_flight_ == kInvalidPacketNumber) {
+        if (!least_in_flight_.IsInitialized()) {
           // At this point, packet_number is in flight and not detected as lost.
           least_in_flight_ = packet_number;
         }
@@ -166,18 +166,19 @@
       packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
       continue;
     }
-    if (least_in_flight_ == kInvalidPacketNumber) {
+    if (!least_in_flight_.IsInitialized()) {
       // At this point, packet_number is in flight and not detected as lost.
       least_in_flight_ = packet_number;
     }
   }
-  if (least_in_flight_ == kInvalidPacketNumber) {
+  if (!least_in_flight_.IsInitialized()) {
     // There is no in flight packet.
     least_in_flight_ = largest_newly_acked + 1;
   }
   largest_previously_acked_ = largest_newly_acked;
   if (!packets_lost->empty()) {
-    DCHECK_LT(largest_lost_, packets_lost->back().packet_number);
+    DCHECK(!largest_lost_.IsInitialized() ||
+           largest_lost_ < packets_lost->back().packet_number);
     largest_lost_ = packets_lost->back().packet_number;
   }
 }
@@ -212,7 +213,8 @@
     return;
   }
 
-  if (spurious_retransmission <= largest_sent_on_spurious_retransmit_) {
+  if (largest_sent_on_spurious_retransmit_.IsInitialized() &&
+      spurious_retransmission <= largest_sent_on_spurious_retransmit_) {
     return;
   }
   largest_sent_on_spurious_retransmit_ = unacked_packets.largest_sent_packet();
diff --git a/net/third_party/quic/core/congestion_control/general_loss_algorithm_test.cc b/net/third_party/quic/core/congestion_control/general_loss_algorithm_test.cc
index daaba8d7..1e3b99bc 100644
--- a/net/third_party/quic/core/congestion_control/general_loss_algorithm_test.cc
+++ b/net/third_party/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -35,33 +35,39 @@
     QuicStreamFrame frame;
     frame.stream_id = QuicUtils::GetHeadersStreamId(
         CurrentSupportedVersions()[0].transport_version);
-    SerializedPacket packet(packet_number, PACKET_1BYTE_PACKET_NUMBER, nullptr,
-                            kDefaultLength, false, false);
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
     packet.retransmittable_frames.push_back(QuicFrame(frame));
-    unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, clock_.Now(),
-                                   true);
+    unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                   NOT_RETRANSMISSION, clock_.Now(), true);
   }
 
-  void SendAckPacket(QuicPacketNumber packet_number) {
-    SerializedPacket packet(packet_number, PACKET_1BYTE_PACKET_NUMBER, nullptr,
-                            kDefaultLength, true, false);
-    unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, clock_.Now(),
-                                   false);
+  void SendAckPacket(uint64_t packet_number) {
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            true, false);
+    unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                   NOT_RETRANSMISSION, clock_.Now(), false);
   }
 
   void VerifyLosses(uint64_t largest_newly_acked,
                     const AckedPacketVector& packets_acked,
                     const std::vector<uint64_t>& losses_expected) {
-    if (largest_newly_acked > unacked_packets_.largest_acked()) {
-      unacked_packets_.IncreaseLargestAcked(largest_newly_acked);
+    if (!unacked_packets_.largest_acked().IsInitialized() ||
+        QuicPacketNumber(largest_newly_acked) >
+            unacked_packets_.largest_acked()) {
+      unacked_packets_.IncreaseLargestAcked(
+          QuicPacketNumber(largest_newly_acked));
     }
     LostPacketVector lost_packets;
     loss_algorithm_.DetectLosses(unacked_packets_, clock_.Now(), rtt_stats_,
-                                 largest_newly_acked, packets_acked,
-                                 &lost_packets);
+                                 QuicPacketNumber(largest_newly_acked),
+                                 packets_acked, &lost_packets);
     ASSERT_EQ(losses_expected.size(), lost_packets.size());
     for (size_t i = 0; i < losses_expected.size(); ++i) {
-      EXPECT_EQ(lost_packets[i].packet_number, losses_expected[i]);
+      EXPECT_EQ(lost_packets[i].packet_number,
+                QuicPacketNumber(losses_expected[i]));
     }
   }
 
@@ -79,18 +85,21 @@
   }
   AckedPacketVector packets_acked;
   // No loss on one ack.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // No loss on two acks.
-  unacked_packets_.RemoveFromInFlight(3);
-  packets_acked.push_back(AckedPacket(3, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(3, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(3, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // Loss on three acks.
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(4, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -105,12 +114,15 @@
   }
   AckedPacketVector packets_acked;
   // Nack the first packet 3 times in a single StretchAck.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  unacked_packets_.RemoveFromInFlight(3);
-  packets_acked.push_back(AckedPacket(3, kMaxPacketSize, QuicTime::Zero()));
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(4, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -124,8 +136,9 @@
   }
   AckedPacketVector packets_acked;
   // Nack the first packet 3 times in an AckFrame with three missing packets.
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(4, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -138,9 +151,10 @@
   }
   AckedPacketVector packets_acked;
   // Early retransmit when the final packet gets acked and the first is nacked.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
             loss_algorithm_.GetLossTimeout());
@@ -162,9 +176,9 @@
   AckedPacketVector packets_acked;
   // Early retransmit when the final packet gets acked and 1.25 RTTs have
   // elapsed since the packets were sent.
-  unacked_packets_.RemoveFromInFlight(kNumSentPackets);
-  packets_acked.push_back(
-      AckedPacket(kNumSentPackets, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(kNumSentPackets));
+  packets_acked.push_back(AckedPacket(QuicPacketNumber(kNumSentPackets),
+                                      kMaxPacketSize, QuicTime::Zero()));
   // This simulates a single ack following multiple missing packets with FACK.
   VerifyLosses(kNumSentPackets, packets_acked, {1, 2});
   packets_acked.clear();
@@ -190,14 +204,15 @@
   }
   AckedPacketVector packets_acked;
   // Neuter packet 1.
-  unacked_packets_.RemoveRetransmittability(1);
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
 
   // Early retransmit when the final packet gets acked and the first is nacked.
-  unacked_packets_.IncreaseLargestAcked(2);
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(),
             loss_algorithm_.GetLossTimeout());
 }
@@ -211,10 +226,11 @@
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
 
   // Early retransmit when the final packet gets acked and the first is nacked.
-  unacked_packets_.IncreaseLargestAcked(2);
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(),
             loss_algorithm_.GetLossTimeout());
@@ -237,9 +253,10 @@
   AckedPacketVector packets_acked;
   // Wait another RTT and ack 2.
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
-  unacked_packets_.IncreaseLargestAcked(2);
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(2, packets_acked, {1});
 }
 
@@ -253,18 +270,21 @@
   }
   AckedPacketVector packets_acked;
   // No loss on one ack.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // No loss on two acks.
-  unacked_packets_.RemoveFromInFlight(3);
-  packets_acked.push_back(AckedPacket(3, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(3, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(3, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // Loss on three acks.
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(4, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -281,19 +301,23 @@
   }
   AckedPacketVector packets_acked;
   // Nack the first packet 3 times in a single StretchAck.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  unacked_packets_.RemoveFromInFlight(3);
-  packets_acked.push_back(AckedPacket(3, kMaxPacketSize, QuicTime::Zero()));
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(4, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(3), kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(4, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // The timer isn't set because we expect more acks.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // Process another ack and then packet 1 will be lost.
-  unacked_packets_.RemoveFromInFlight(5);
-  packets_acked.push_back(AckedPacket(5, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(5), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(5, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -308,15 +332,17 @@
   }
   AckedPacketVector packets_acked;
   // Nack the first packet 3 times in an AckFrame with three missing packets.
-  unacked_packets_.RemoveFromInFlight(4);
-  packets_acked.push_back(AckedPacket(4, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(4, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(4), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(4, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // The timer isn't set because we expect more acks.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // Process another ack and then packet 1 and 2 will be lost.
-  unacked_packets_.RemoveFromInFlight(5);
-  packets_acked.push_back(AckedPacket(5, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(5), kMaxPacketSize, QuicTime::Zero()));
   VerifyLosses(5, packets_acked, {1, 2});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -330,10 +356,11 @@
     SendDataPacket(i);
   }
   AckedPacketVector packets_acked;
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
   for (size_t i = 1; i < 500; ++i) {
-    VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+    VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
     packets_acked.clear();
   }
   if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
@@ -357,9 +384,10 @@
   // Expect the timer to not be set.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // The packet should not be lost until 1.25 RTTs pass.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
     // Expect the timer to be set to 0.25 RTT's in the future.
@@ -370,7 +398,7 @@
     EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(),
               loss_algorithm_.GetLossTimeout() - clock_.Now());
   }
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
   VerifyLosses(2, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
@@ -388,16 +416,17 @@
   // Expect the timer to not be set.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // The packet should not be lost without a nack.
-  unacked_packets_.RemoveFromInFlight(1);
-  packets_acked.push_back(AckedPacket(1, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(1, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(1), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // The timer should still not be set.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
-  VerifyLosses(1, packets_acked, std::vector<QuicPacketNumber>{});
+  VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
-  VerifyLosses(1, packets_acked, std::vector<QuicPacketNumber>{});
+  VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
 
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
 }
@@ -414,9 +443,10 @@
   // Expect the timer to not be set.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // The packet should not be lost until 1.25 RTTs pass.
-  unacked_packets_.RemoveFromInFlight(10);
-  packets_acked.push_back(AckedPacket(10, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(10, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(10), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(10, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
     // Expect the timer to be set to 0.25 RTT's in the future.
@@ -445,9 +475,10 @@
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // The packet should not be lost until 1.25 RTTs pass.
 
-  unacked_packets_.RemoveFromInFlight(10);
-  packets_acked.push_back(AckedPacket(10, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(10, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(10), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(10, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
     // Expect the timer to be set to 0.25 RTT's in the future.
@@ -461,10 +492,11 @@
   clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
   // Now ack packets 1 to 9 and ensure the timer is no longer set and no packets
   // are lost.
-  for (QuicPacketNumber i = 1; i <= 9; ++i) {
-    unacked_packets_.RemoveFromInFlight(i);
-    packets_acked.push_back(AckedPacket(i, kMaxPacketSize, QuicTime::Zero()));
-    VerifyLosses(i, packets_acked, std::vector<QuicPacketNumber>{});
+  for (uint64_t i = 1; i <= 9; ++i) {
+    unacked_packets_.RemoveFromInFlight(QuicPacketNumber(i));
+    packets_acked.push_back(
+        AckedPacket(QuicPacketNumber(i), kMaxPacketSize, QuicTime::Zero()));
+    VerifyLosses(i, packets_acked, std::vector<uint64_t>{});
     packets_acked.clear();
     EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   }
@@ -484,14 +516,15 @@
   // Expect the timer to not be set.
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
   // Packet 1 should not be lost until 1/16 RTTs pass.
-  unacked_packets_.RemoveFromInFlight(2);
-  packets_acked.push_back(AckedPacket(2, kMaxPacketSize, QuicTime::Zero()));
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(
+      AckedPacket(QuicPacketNumber(2), kMaxPacketSize, QuicTime::Zero()));
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   packets_acked.clear();
   // Expect the timer to be set to 1/16 RTT's in the future.
   EXPECT_EQ(rtt_stats_.smoothed_rtt() * (1.0f / 16),
             loss_algorithm_.GetLossTimeout() - clock_.Now());
-  VerifyLosses(2, packets_acked, std::vector<QuicPacketNumber>{});
+  VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 16));
   VerifyLosses(2, packets_acked, {1});
   EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
@@ -508,13 +541,13 @@
     clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
   }
   loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(),
-                                             rtt_stats_, 11);
+                                             rtt_stats_, QuicPacketNumber(11));
   EXPECT_EQ(1, loss_algorithm_.reordering_shift());
 
   // Detect another spurious retransmit and ensure the threshold doesn't
   // increase again.
   loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(),
-                                             rtt_stats_, 12);
+                                             rtt_stats_, QuicPacketNumber(12));
   EXPECT_EQ(1, loss_algorithm_.reordering_shift());
 }
 
diff --git a/net/third_party/quic/core/congestion_control/hybrid_slow_start.cc b/net/third_party/quic/core/congestion_control/hybrid_slow_start.cc
index 29d07a1..16a5f4b 100644
--- a/net/third_party/quic/core/congestion_control/hybrid_slow_start.cc
+++ b/net/third_party/quic/core/congestion_control/hybrid_slow_start.cc
@@ -24,8 +24,6 @@
 HybridSlowStart::HybridSlowStart()
     : started_(false),
       hystart_found_(NOT_FOUND),
-      last_sent_packet_number_(0),
-      end_packet_number_(0),
       rtt_sample_count_(0),
       current_min_rtt_(QuicTime::Delta::Zero()) {}
 
@@ -56,7 +54,7 @@
 }
 
 bool HybridSlowStart::IsEndOfRound(QuicPacketNumber ack) const {
-  return end_packet_number_ <= ack;
+  return !end_packet_number_.IsInitialized() || end_packet_number_ <= ack;
 }
 
 bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt,
diff --git a/net/third_party/quic/core/congestion_control/hybrid_slow_start_test.cc b/net/third_party/quic/core/congestion_control/hybrid_slow_start_test.cc
index 2ff614d5..5118e87 100644
--- a/net/third_party/quic/core/congestion_control/hybrid_slow_start_test.cc
+++ b/net/third_party/quic/core/congestion_control/hybrid_slow_start_test.cc
@@ -24,8 +24,8 @@
 };
 
 TEST_F(HybridSlowStartTest, Simple) {
-  QuicPacketNumber packet_number = 1;
-  QuicPacketNumber end_packet_number = 3;
+  QuicPacketNumber packet_number(1);
+  QuicPacketNumber end_packet_number(3);
   slow_start_->StartReceiveRound(end_packet_number);
 
   EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++));
@@ -39,7 +39,7 @@
   // Test without a new registered end_packet_number;
   EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++));
 
-  end_packet_number = 20;
+  end_packet_number = QuicPacketNumber(20);
   slow_start_->StartReceiveRound(end_packet_number);
   while (packet_number < end_packet_number) {
     EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++));
@@ -52,7 +52,7 @@
   // RTT of 60ms the detection will happen at 67.5 ms.
   const int kHybridStartMinSamples = 8;  // Number of acks required to trigger.
 
-  QuicPacketNumber end_packet_number = 1;
+  QuicPacketNumber end_packet_number(1);
   slow_start_->StartReceiveRound(end_packet_number++);
 
   // Will not trigger since our lowest RTT in our burst is the same as the long
diff --git a/net/third_party/quic/core/congestion_control/pacing_sender_test.cc b/net/third_party/quic/core/congestion_control/pacing_sender_test.cc
index 5648b14..1aeeabe 100644
--- a/net/third_party/quic/core/congestion_control/pacing_sender_test.cc
+++ b/net/third_party/quic/core/congestion_control/pacing_sender_test.cc
@@ -50,7 +50,7 @@
     if (burst_size == 0) {
       EXPECT_CALL(*mock_sender_, OnCongestionEvent(_, _, _, _, _));
       LostPacketVector lost_packets;
-      lost_packets.push_back(LostPacket(1, kMaxPacketSize));
+      lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize));
       AckedPacketVector empty;
       pacing_sender_->OnCongestionEvent(true, 1234, clock_.Now(), empty,
                                         lost_packets);
@@ -320,7 +320,7 @@
 
   // Losing a packet will set clear burst tokens.
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(1, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize));
   AckedPacketVector empty_acked;
   EXPECT_CALL(*mock_sender_,
               OnCongestionEvent(true, kMaxPacketSize, _, IsEmpty(), _));
diff --git a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
index d5279ba..02f4bf4 100644
--- a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
+++ b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
@@ -38,9 +38,6 @@
       stats_(stats),
       reno_(reno),
       num_connections_(kDefaultNumConnections),
-      largest_sent_packet_number_(kInvalidPacketNumber),
-      largest_acked_packet_number_(kInvalidPacketNumber),
-      largest_sent_at_last_cutback_(kInvalidPacketNumber),
       min4_mode_(false),
       last_cutback_exited_slowstart_(false),
       slow_start_large_reduction_(false),
@@ -151,8 +148,12 @@
                                         QuicByteCount acked_bytes,
                                         QuicByteCount prior_in_flight,
                                         QuicTime event_time) {
-  largest_acked_packet_number_ =
-      std::max(acked_packet_number, largest_acked_packet_number_);
+  if (largest_acked_packet_number_.IsInitialized()) {
+    largest_acked_packet_number_ =
+        std::max(acked_packet_number, largest_acked_packet_number_);
+  } else {
+    largest_acked_packet_number_ = acked_packet_number;
+  }
   if (InRecovery()) {
     if (!no_prr_) {
       // PRR is used when in recovery.
@@ -184,7 +185,8 @@
     // PRR is used when in recovery.
     prr_.OnPacketSent(bytes);
   }
-  DCHECK_LT(largest_sent_packet_number_, packet_number);
+  DCHECK(!largest_sent_packet_number_.IsInitialized() ||
+         largest_sent_packet_number_ < packet_number);
   largest_sent_packet_number_ = packet_number;
   hybrid_slow_start_.OnPacketSent(packet_number);
 }
@@ -240,8 +242,9 @@
 }
 
 bool TcpCubicSenderBytes::InRecovery() const {
-  return largest_acked_packet_number_ <= largest_sent_at_last_cutback_ &&
-         largest_acked_packet_number_ != kInvalidPacketNumber;
+  return largest_acked_packet_number_.IsInitialized() &&
+         largest_sent_at_last_cutback_.IsInitialized() &&
+         largest_acked_packet_number_ <= largest_sent_at_last_cutback_;
 }
 
 bool TcpCubicSenderBytes::ShouldSendProbingPacket() const {
@@ -249,7 +252,7 @@
 }
 
 void TcpCubicSenderBytes::OnRetransmissionTimeout(bool packets_retransmitted) {
-  largest_sent_at_last_cutback_ = kInvalidPacketNumber;
+  largest_sent_at_last_cutback_.Clear();
   if (!packets_retransmitted) {
     return;
   }
@@ -298,7 +301,8 @@
                                        QuicByteCount prior_in_flight) {
   // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
   // already sent should be treated as a single loss event, since it's expected.
-  if (packet_number <= largest_sent_at_last_cutback_) {
+  if (largest_sent_at_last_cutback_.IsInitialized() &&
+      packet_number <= largest_sent_at_last_cutback_) {
     if (last_cutback_exited_slowstart_) {
       ++stats_->slowstart_packets_lost;
       stats_->slowstart_bytes_lost += lost_bytes;
@@ -414,9 +418,9 @@
 void TcpCubicSenderBytes::OnConnectionMigration() {
   hybrid_slow_start_.Restart();
   prr_ = PrrSender();
-  largest_sent_packet_number_ = kInvalidPacketNumber;
-  largest_acked_packet_number_ = kInvalidPacketNumber;
-  largest_sent_at_last_cutback_ = kInvalidPacketNumber;
+  largest_sent_packet_number_.Clear();
+  largest_acked_packet_number_.Clear();
+  largest_sent_at_last_cutback_.Clear();
   last_cutback_exited_slowstart_ = false;
   cubic_.ResetCubicState();
   num_acked_packets_ = 0;
diff --git a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
index d35b6a9..2da2328 100644
--- a/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
+++ b/net/third_party/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
@@ -69,8 +69,9 @@
     int packets_sent = 0;
     bool can_send = sender_->CanSend(bytes_in_flight_);
     while (can_send) {
-      sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, packet_number_++,
-                            kDefaultTCPMSS, HAS_RETRANSMITTABLE_DATA);
+      sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+                            QuicPacketNumber(packet_number_++), kDefaultTCPMSS,
+                            HAS_RETRANSMITTABLE_DATA);
       ++packets_sent;
       bytes_in_flight_ += kDefaultTCPMSS;
       can_send = sender_->CanSend(bytes_in_flight_);
@@ -87,7 +88,8 @@
     for (int i = 0; i < n; ++i) {
       ++acked_packet_number_;
       acked_packets.push_back(
-          AckedPacket(acked_packet_number_, kDefaultTCPMSS, QuicTime::Zero()));
+          AckedPacket(QuicPacketNumber(acked_packet_number_), kDefaultTCPMSS,
+                      QuicTime::Zero()));
     }
     sender_->OnCongestionEvent(true, bytes_in_flight_, clock_.Now(),
                                acked_packets, lost_packets);
@@ -102,7 +104,8 @@
     LostPacketVector lost_packets;
     for (int i = 0; i < n; ++i) {
       ++acked_packet_number_;
-      lost_packets.push_back(LostPacket(acked_packet_number_, packet_length));
+      lost_packets.push_back(
+          LostPacket(QuicPacketNumber(acked_packet_number_), packet_length));
     }
     sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(),
                                acked_packets, lost_packets);
@@ -113,7 +116,8 @@
   void LosePacket(uint64_t packet_number) {
     AckedPacketVector acked_packets;
     LostPacketVector lost_packets;
-    lost_packets.push_back(LostPacket(packet_number, kDefaultTCPMSS));
+    lost_packets.push_back(
+        LostPacket(QuicPacketNumber(packet_number), kDefaultTCPMSS));
     sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(),
                                acked_packets, lost_packets);
     bytes_in_flight_ -= kDefaultTCPMSS;
@@ -122,7 +126,7 @@
   const QuicTime::Delta one_ms_;
   MockClock clock_;
   std::unique_ptr<TcpCubicSenderBytesPeer> sender_;
-  QuicPacketNumber packet_number_;
+  uint64_t packet_number_;
   uint64_t acked_packet_number_;
   QuicByteCount bytes_in_flight_;
 };
@@ -779,7 +783,8 @@
   LostPacketVector missing_packets;
   for (uint64_t i = 1; i < kDefaultMaxCongestionWindowPackets; ++i) {
     acked_packets.clear();
-    acked_packets.push_back(AckedPacket(i, 1350, QuicTime::Zero()));
+    acked_packets.push_back(
+        AckedPacket(QuicPacketNumber(i), 1350, QuicTime::Zero()));
     sender->OnCongestionEvent(true, sender->GetCongestionWindow(), clock_.Now(),
                               acked_packets, missing_packets);
   }
diff --git a/net/third_party/quic/core/frames/quic_ack_frame.cc b/net/third_party/quic/core/frames/quic_ack_frame.cc
index 9cd370e..c240fe1 100644
--- a/net/third_party/quic/core/frames/quic_ack_frame.cc
+++ b/net/third_party/quic/core/frames/quic_ack_frame.cc
@@ -12,20 +12,29 @@
 namespace quic {
 
 namespace {
+
 const QuicPacketCount kMaxPrintRange = 128;
+
+uint64_t PacketNumberIntervalLength(
+    const QuicInterval<QuicPacketNumber>& interval) {
+  if (interval.Empty()) {
+    return 0u;
+  }
+  return interval.max() - interval.min();
+}
 }  // namespace
 
 bool IsAwaitingPacket(const QuicAckFrame& ack_frame,
                       QuicPacketNumber packet_number,
                       QuicPacketNumber peer_least_packet_awaiting_ack) {
-  DCHECK_NE(kInvalidPacketNumber, packet_number);
-  return packet_number >= peer_least_packet_awaiting_ack &&
+  DCHECK(packet_number.IsInitialized());
+  return (!peer_least_packet_awaiting_ack.IsInitialized() ||
+          packet_number >= peer_least_packet_awaiting_ack) &&
          !ack_frame.packets.Contains(packet_number);
 }
 
 QuicAckFrame::QuicAckFrame()
-    : largest_acked(kInvalidPacketNumber),
-      ack_delay_time(QuicTime::Delta::Infinite()),
+    : ack_delay_time(QuicTime::Delta::Infinite()),
       ecn_counters_populated(false),
       ect_0_count(0),
       ect_1_count(0),
@@ -57,7 +66,7 @@
 }
 
 void QuicAckFrame::Clear() {
-  largest_acked = kInvalidPacketNumber;
+  largest_acked.Clear();
   ack_delay_time = QuicTime::Delta::Infinite();
   received_packet_times.clear();
   packets.Clear();
@@ -74,7 +83,7 @@
     default;
 
 void PacketNumberQueue::Add(QuicPacketNumber packet_number) {
-  if (packet_number == kInvalidPacketNumber) {
+  if (!packet_number.IsInitialized()) {
     return;
   }
   // Check if the deque is empty
@@ -152,8 +161,7 @@
 
 void PacketNumberQueue::AddRange(QuicPacketNumber lower,
                                  QuicPacketNumber higher) {
-  if (lower == kInvalidPacketNumber || higher == kInvalidPacketNumber ||
-      lower >= higher) {
+  if (!lower.IsInitialized() || !higher.IsInitialized() || lower >= higher) {
     return;
   }
   if (packet_number_deque_.empty()) {
@@ -192,7 +200,7 @@
 }
 
 bool PacketNumberQueue::RemoveUpTo(QuicPacketNumber higher) {
-  if (higher == kInvalidPacketNumber || Empty()) {
+  if (!higher.IsInitialized() || Empty()) {
     return false;
   }
   const QuicPacketNumber old_min = Min();
@@ -226,7 +234,7 @@
 }
 
 bool PacketNumberQueue::Contains(QuicPacketNumber packet_number) const {
-  if (packet_number == kInvalidPacketNumber || packet_number_deque_.empty()) {
+  if (!packet_number.IsInitialized() || packet_number_deque_.empty()) {
     return false;
   }
   if (packet_number_deque_.front().min() > packet_number ||
@@ -258,7 +266,7 @@
 QuicPacketCount PacketNumberQueue::NumPacketsSlow() const {
   QuicPacketCount n_packets = 0;
   for (QuicInterval<QuicPacketNumber> interval : packet_number_deque_) {
-    n_packets += interval.Length();
+    n_packets += PacketNumberIntervalLength(interval);
   }
   return n_packets;
 }
@@ -285,7 +293,7 @@
 
 QuicPacketCount PacketNumberQueue::LastIntervalLength() const {
   DCHECK(!Empty());
-  return packet_number_deque_.back().Length();
+  return PacketNumberIntervalLength(packet_number_deque_.back());
 }
 
 // Largest min...max range for packet numbers where we print the numbers
diff --git a/net/third_party/quic/core/frames/quic_frames_test.cc b/net/third_party/quic/core/frames/quic_frames_test.cc
index fc67e10..fd4091b 100644
--- a/net/third_party/quic/core/frames/quic_frames_test.cc
+++ b/net/third_party/quic/core/frames/quic_frames_test.cc
@@ -27,12 +27,13 @@
 
 TEST_F(QuicFramesTest, AckFrameToString) {
   QuicAckFrame frame;
-  frame.largest_acked = 5;
+  frame.largest_acked = QuicPacketNumber(5);
   frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3);
-  frame.packets.Add(4);
-  frame.packets.Add(5);
+  frame.packets.Add(QuicPacketNumber(4));
+  frame.packets.Add(QuicPacketNumber(5));
   frame.received_packet_times = {
-      {6, QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
+      {QuicPacketNumber(6),
+       QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
   std::ostringstream stream;
   stream << frame;
   EXPECT_EQ(
@@ -45,11 +46,12 @@
 
 TEST_F(QuicFramesTest, BigAckFrameToString) {
   QuicAckFrame frame;
-  frame.largest_acked = 500;
+  frame.largest_acked = QuicPacketNumber(500);
   frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3);
-  frame.packets.AddRange(4, 501);
+  frame.packets.AddRange(QuicPacketNumber(4), QuicPacketNumber(501));
   frame.received_packet_times = {
-      {500, QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
+      {QuicPacketNumber(500),
+       QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
   std::ostringstream stream;
   stream << frame;
   EXPECT_EQ(
@@ -213,7 +215,7 @@
 
 TEST_F(QuicFramesTest, StopWaitingFrameToString) {
   QuicStopWaitingFrame frame;
-  frame.least_unacked = 2;
+  frame.least_unacked = QuicPacketNumber(2);
   std::ostringstream stream;
   stream << frame;
   EXPECT_EQ("{ least_unacked: 2 }\n", stream.str());
@@ -223,69 +225,84 @@
 
 TEST_F(QuicFramesTest, IsAwaitingPacket) {
   QuicAckFrame ack_frame1;
-  ack_frame1.largest_acked = 10u;
-  ack_frame1.packets.AddRange(1, 11);
-  EXPECT_TRUE(IsAwaitingPacket(ack_frame1, 11u, 0u));
-  EXPECT_FALSE(IsAwaitingPacket(ack_frame1, 1u, 0u));
+  ack_frame1.largest_acked = QuicPacketNumber(10u);
+  ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(11));
+  EXPECT_TRUE(
+      IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber()));
+  EXPECT_FALSE(
+      IsAwaitingPacket(ack_frame1, QuicPacketNumber(1u), QuicPacketNumber()));
 
-  ack_frame1.packets.Add(12);
-  EXPECT_TRUE(IsAwaitingPacket(ack_frame1, 11u, 0u));
+  ack_frame1.packets.Add(QuicPacketNumber(12));
+  EXPECT_TRUE(
+      IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber()));
 
   QuicAckFrame ack_frame2;
-  ack_frame2.largest_acked = 100u;
-  ack_frame2.packets.AddRange(21, 100);
-  EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 11u, 20u));
-  EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 80u, 20u));
-  EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u));
+  ack_frame2.largest_acked = QuicPacketNumber(100u);
+  ack_frame2.packets.AddRange(QuicPacketNumber(21), QuicPacketNumber(100));
+  EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(11u),
+                                QuicPacketNumber(20u)));
+  EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(80u),
+                                QuicPacketNumber(20u)));
+  EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u),
+                               QuicPacketNumber(20u)));
 
-  ack_frame2.packets.AddRange(102, 200);
-  EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u));
+  ack_frame2.packets.AddRange(QuicPacketNumber(102), QuicPacketNumber(200));
+  EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u),
+                               QuicPacketNumber(20u)));
 }
 
 TEST_F(QuicFramesTest, AddPacket) {
   QuicAckFrame ack_frame1;
-  ack_frame1.packets.Add(1);
-  ack_frame1.packets.Add(99);
+  ack_frame1.packets.Add(QuicPacketNumber(1));
+  ack_frame1.packets.Add(QuicPacketNumber(99));
 
   EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
-  EXPECT_EQ(1u, ack_frame1.packets.Min());
-  EXPECT_EQ(99u, ack_frame1.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(1, 2));
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(99, 100));
+  expected_intervals.emplace_back(
+      QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(99), QuicPacketNumber(100)));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
   EXPECT_EQ(expected_intervals, actual_intervals);
 
-  ack_frame1.packets.Add(20);
+  ack_frame1.packets.Add(QuicPacketNumber(20));
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2;
-  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(1, 2));
-  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(20, 21));
-  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(99, 100));
+  expected_intervals2.emplace_back(
+      QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(20), QuicPacketNumber(21)));
+  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(99), QuicPacketNumber(100)));
 
   EXPECT_EQ(3u, ack_frame1.packets.NumIntervals());
   EXPECT_EQ(expected_intervals2, actual_intervals2);
 
-  ack_frame1.packets.Add(19);
-  ack_frame1.packets.Add(21);
+  ack_frame1.packets.Add(QuicPacketNumber(19));
+  ack_frame1.packets.Add(QuicPacketNumber(21));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals3(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals3;
-  expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(1, 2));
-  expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(19, 22));
-  expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(99, 100));
+  expected_intervals3.emplace_back(
+      QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+  expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(19), QuicPacketNumber(22)));
+  expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(99), QuicPacketNumber(100)));
 
   EXPECT_EQ(expected_intervals3, actual_intervals3);
 
-  ack_frame1.packets.Add(20);
+  ack_frame1.packets.Add(QuicPacketNumber(20));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals4(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -293,37 +310,44 @@
   EXPECT_EQ(expected_intervals3, actual_intervals4);
 
   QuicAckFrame ack_frame2;
-  ack_frame2.packets.Add(20);
-  ack_frame2.packets.Add(40);
-  ack_frame2.packets.Add(60);
-  ack_frame2.packets.Add(10);
-  ack_frame2.packets.Add(80);
+  ack_frame2.packets.Add(QuicPacketNumber(20));
+  ack_frame2.packets.Add(QuicPacketNumber(40));
+  ack_frame2.packets.Add(QuicPacketNumber(60));
+  ack_frame2.packets.Add(QuicPacketNumber(10));
+  ack_frame2.packets.Add(QuicPacketNumber(80));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals5(
       ack_frame2.packets.begin(), ack_frame2.packets.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals5;
-  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(10, 11));
-  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(20, 21));
-  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(40, 41));
-  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(60, 61));
-  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(80, 81));
+  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(10), QuicPacketNumber(11)));
+  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(20), QuicPacketNumber(21)));
+  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(40), QuicPacketNumber(41)));
+  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(60), QuicPacketNumber(61)));
+  expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(80), QuicPacketNumber(81)));
 
   EXPECT_EQ(expected_intervals5, actual_intervals5);
 }
 
 TEST_F(QuicFramesTest, AddInterval) {
   QuicAckFrame ack_frame1;
-  ack_frame1.packets.AddRange(1, 10);
-  ack_frame1.packets.AddRange(50, 100);
+  ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(10));
+  ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(100));
 
   EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
-  EXPECT_EQ(1u, ack_frame1.packets.Min());
-  EXPECT_EQ(99u, ack_frame1.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(1, 10));
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(50, 100));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(1), QuicPacketNumber(10)));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(50), QuicPacketNumber(100)));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -331,48 +355,58 @@
   EXPECT_EQ(expected_intervals, actual_intervals);
 
   // Ensure adding a range within the existing ranges fails.
-  EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(20, 30), "");
+  EXPECT_QUIC_BUG(
+      ack_frame1.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(30)),
+      "");
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2;
-  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(1, 10));
-  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(50, 100));
+  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(1), QuicPacketNumber(10)));
+  expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(50), QuicPacketNumber(100)));
 
   EXPECT_EQ(expected_intervals2.size(), ack_frame1.packets.NumIntervals());
   EXPECT_EQ(expected_intervals2, actual_intervals2);
 
   // Add ranges at both ends.
   QuicAckFrame ack_frame2;
-  ack_frame2.packets.AddRange(20, 25);
-  ack_frame2.packets.AddRange(40, 45);
-  ack_frame2.packets.AddRange(60, 65);
-  ack_frame2.packets.AddRange(10, 15);
-  ack_frame2.packets.AddRange(80, 85);
+  ack_frame2.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(25));
+  ack_frame2.packets.AddRange(QuicPacketNumber(40), QuicPacketNumber(45));
+  ack_frame2.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(65));
+  ack_frame2.packets.AddRange(QuicPacketNumber(10), QuicPacketNumber(15));
+  ack_frame2.packets.AddRange(QuicPacketNumber(80), QuicPacketNumber(85));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals8(
       ack_frame2.packets.begin(), ack_frame2.packets.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals8;
-  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(10, 15));
-  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(20, 25));
-  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(40, 45));
-  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(60, 65));
-  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(80, 85));
+  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(10), QuicPacketNumber(15)));
+  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(20), QuicPacketNumber(25)));
+  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(40), QuicPacketNumber(45)));
+  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(60), QuicPacketNumber(65)));
+  expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(80), QuicPacketNumber(85)));
 
   EXPECT_EQ(expected_intervals8, actual_intervals8);
 }
 
 TEST_F(QuicFramesTest, AddAdjacentForward) {
   QuicAckFrame ack_frame1;
-  ack_frame1.packets.Add(49);
-  ack_frame1.packets.AddRange(50, 60);
-  ack_frame1.packets.AddRange(60, 70);
-  ack_frame1.packets.AddRange(70, 100);
+  ack_frame1.packets.Add(QuicPacketNumber(49));
+  ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60));
+  ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70));
+  ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100));
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(49, 100));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(49), QuicPacketNumber(100)));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -382,13 +416,14 @@
 
 TEST_F(QuicFramesTest, AddAdjacentReverse) {
   QuicAckFrame ack_frame1;
-  ack_frame1.packets.AddRange(70, 100);
-  ack_frame1.packets.AddRange(60, 70);
-  ack_frame1.packets.AddRange(50, 60);
-  ack_frame1.packets.Add(49);
+  ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100));
+  ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70));
+  ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60));
+  ack_frame1.packets.Add(QuicPacketNumber(49));
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(49, 100));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(49), QuicPacketNumber(100)));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       ack_frame1.packets.begin(), ack_frame1.packets.end());
@@ -398,19 +433,19 @@
 
 TEST_F(QuicFramesTest, RemoveSmallestInterval) {
   QuicAckFrame ack_frame1;
-  ack_frame1.largest_acked = 100u;
-  ack_frame1.packets.AddRange(51, 60);
-  ack_frame1.packets.AddRange(71, 80);
-  ack_frame1.packets.AddRange(91, 100);
+  ack_frame1.largest_acked = QuicPacketNumber(100u);
+  ack_frame1.packets.AddRange(QuicPacketNumber(51), QuicPacketNumber(60));
+  ack_frame1.packets.AddRange(QuicPacketNumber(71), QuicPacketNumber(80));
+  ack_frame1.packets.AddRange(QuicPacketNumber(91), QuicPacketNumber(100));
   ack_frame1.packets.RemoveSmallestInterval();
   EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
-  EXPECT_EQ(71u, ack_frame1.packets.Min());
-  EXPECT_EQ(99u, ack_frame1.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(71u), ack_frame1.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
 
   ack_frame1.packets.RemoveSmallestInterval();
   EXPECT_EQ(1u, ack_frame1.packets.NumIntervals());
-  EXPECT_EQ(91u, ack_frame1.packets.Min());
-  EXPECT_EQ(99u, ack_frame1.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(91u), ack_frame1.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
 }
 
 class PacketNumberQueueTest : public QuicTest {};
@@ -418,85 +453,85 @@
 // Tests that a queue contains the expected data after calls to Add().
 TEST_F(PacketNumberQueueTest, AddRange) {
   PacketNumberQueue queue;
-  queue.AddRange(1, 51);
-  queue.Add(53);
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(51));
+  queue.Add(QuicPacketNumber(53));
 
-  EXPECT_FALSE(queue.Contains(0));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
   for (int i = 1; i < 51; ++i) {
-    EXPECT_TRUE(queue.Contains(i));
+    EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
   }
-  EXPECT_FALSE(queue.Contains(51));
-  EXPECT_FALSE(queue.Contains(52));
-  EXPECT_TRUE(queue.Contains(53));
-  EXPECT_FALSE(queue.Contains(54));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber(51)));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber(52)));
+  EXPECT_TRUE(queue.Contains(QuicPacketNumber(53)));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber(54)));
   EXPECT_EQ(51u, queue.NumPacketsSlow());
-  EXPECT_EQ(1u, queue.Min());
-  EXPECT_EQ(53u, queue.Max());
+  EXPECT_EQ(QuicPacketNumber(1u), queue.Min());
+  EXPECT_EQ(QuicPacketNumber(53u), queue.Max());
 
-  queue.Add(70);
-  EXPECT_EQ(70u, queue.Max());
+  queue.Add(QuicPacketNumber(70));
+  EXPECT_EQ(QuicPacketNumber(70u), queue.Max());
 }
 
 // Tests Contains function
 TEST_F(PacketNumberQueueTest, Contains) {
   PacketNumberQueue queue;
-  EXPECT_FALSE(queue.Contains(0));
-  queue.AddRange(5, 10);
-  queue.Add(20);
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
+  queue.AddRange(QuicPacketNumber(5), QuicPacketNumber(10));
+  queue.Add(QuicPacketNumber(20));
 
   for (int i = 1; i < 5; ++i) {
-    EXPECT_FALSE(queue.Contains(i));
+    EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
   }
 
   for (int i = 5; i < 10; ++i) {
-    EXPECT_TRUE(queue.Contains(i));
+    EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
   }
   for (int i = 10; i < 20; ++i) {
-    EXPECT_FALSE(queue.Contains(i));
+    EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
   }
-  EXPECT_TRUE(queue.Contains(20));
-  EXPECT_FALSE(queue.Contains(21));
+  EXPECT_TRUE(queue.Contains(QuicPacketNumber(20)));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber(21)));
 
   PacketNumberQueue queue2;
-  EXPECT_FALSE(queue2.Contains(1));
+  EXPECT_FALSE(queue2.Contains(QuicPacketNumber(1)));
   for (int i = 1; i < 51; ++i) {
-    queue2.Add(2 * i);
+    queue2.Add(QuicPacketNumber(2 * i));
   }
-  EXPECT_FALSE(queue2.Contains(0));
+  EXPECT_FALSE(queue2.Contains(QuicPacketNumber()));
   for (int i = 1; i < 51; ++i) {
     if (i % 2 == 0) {
-      EXPECT_TRUE(queue2.Contains(i));
+      EXPECT_TRUE(queue2.Contains(QuicPacketNumber(i)));
     } else {
-      EXPECT_FALSE(queue2.Contains(i));
+      EXPECT_FALSE(queue2.Contains(QuicPacketNumber(i)));
     }
   }
-  EXPECT_FALSE(queue2.Contains(101));
+  EXPECT_FALSE(queue2.Contains(QuicPacketNumber(101)));
 }
 
 // Tests that a queue contains the expected data after calls to RemoveUpTo().
 TEST_F(PacketNumberQueueTest, Removal) {
   PacketNumberQueue queue;
-  EXPECT_FALSE(queue.Contains(51));
-  queue.AddRange(1, 100);
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber(51)));
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
 
-  EXPECT_TRUE(queue.RemoveUpTo(51));
-  EXPECT_FALSE(queue.RemoveUpTo(51));
+  EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(51)));
+  EXPECT_FALSE(queue.RemoveUpTo(QuicPacketNumber(51)));
 
-  EXPECT_FALSE(queue.Contains(0));
+  EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
   for (int i = 1; i < 51; ++i) {
-    EXPECT_FALSE(queue.Contains(i));
+    EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
   }
   for (int i = 51; i < 100; ++i) {
-    EXPECT_TRUE(queue.Contains(i));
+    EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
   }
   EXPECT_EQ(49u, queue.NumPacketsSlow());
-  EXPECT_EQ(51u, queue.Min());
-  EXPECT_EQ(99u, queue.Max());
+  EXPECT_EQ(QuicPacketNumber(51u), queue.Min());
+  EXPECT_EQ(QuicPacketNumber(99u), queue.Max());
 
   PacketNumberQueue queue2;
-  queue2.AddRange(1, 5);
-  EXPECT_TRUE(queue2.RemoveUpTo(3));
-  EXPECT_TRUE(queue2.RemoveUpTo(50));
+  queue2.AddRange(QuicPacketNumber(1), QuicPacketNumber(5));
+  EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(3)));
+  EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(50)));
   EXPECT_TRUE(queue2.Empty());
 }
 
@@ -506,8 +541,8 @@
   EXPECT_TRUE(queue.Empty());
   EXPECT_EQ(0u, queue.NumPacketsSlow());
 
-  queue.AddRange(1, 100);
-  EXPECT_TRUE(queue.RemoveUpTo(100));
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
+  EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(100)));
   EXPECT_TRUE(queue.Empty());
   EXPECT_EQ(0u, queue.NumPacketsSlow());
 }
@@ -518,29 +553,30 @@
   PacketNumberQueue queue;
   oss << queue;
 
-  queue.Add(1);
-  queue.AddRange(50, 100);
+  queue.Add(QuicPacketNumber(1));
+  queue.AddRange(QuicPacketNumber(50), QuicPacketNumber(100));
   oss << queue;
 }
 
 // Tests that the iterators returned from a packet queue iterate over the queue.
 TEST_F(PacketNumberQueueTest, Iterators) {
   PacketNumberQueue queue;
-  queue.AddRange(1, 100);
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       queue.begin(), queue.end());
 
   PacketNumberQueue queue2;
   for (int i = 1; i < 100; i++) {
-    queue2.AddRange(i, i + 1);
+    queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1));
   }
 
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
       queue2.begin(), queue2.end());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(1, 100));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(1), QuicPacketNumber(100)));
   EXPECT_EQ(expected_intervals, actual_intervals);
   EXPECT_EQ(expected_intervals, actual_intervals2);
   EXPECT_EQ(actual_intervals, actual_intervals2);
@@ -548,10 +584,10 @@
 
 TEST_F(PacketNumberQueueTest, ReversedIterators) {
   PacketNumberQueue queue;
-  queue.AddRange(1, 100);
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
   PacketNumberQueue queue2;
   for (int i = 1; i < 100; i++) {
-    queue2.AddRange(i, i + 1);
+    queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1));
   }
   const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
       queue.rbegin(), queue.rend());
@@ -559,7 +595,8 @@
       queue2.rbegin(), queue2.rend());
 
   std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
-  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(1, 100));
+  expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+      QuicPacketNumber(1), QuicPacketNumber(100)));
 
   EXPECT_EQ(expected_intervals, actual_intervals);
   EXPECT_EQ(expected_intervals, actual_intervals2);
@@ -567,7 +604,7 @@
 
   PacketNumberQueue queue3;
   for (int i = 1; i < 20; i++) {
-    queue3.Add(2 * i);
+    queue3.Add(QuicPacketNumber(2 * i));
   }
 
   auto begin = queue3.begin();
@@ -583,17 +620,17 @@
 
 TEST_F(PacketNumberQueueTest, IntervalLengthAndRemoveInterval) {
   PacketNumberQueue queue;
-  queue.AddRange(1, 10);
-  queue.AddRange(20, 30);
-  queue.AddRange(40, 50);
+  queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(10));
+  queue.AddRange(QuicPacketNumber(20), QuicPacketNumber(30));
+  queue.AddRange(QuicPacketNumber(40), QuicPacketNumber(50));
   EXPECT_EQ(3u, queue.NumIntervals());
   EXPECT_EQ(10u, queue.LastIntervalLength());
 
-  EXPECT_TRUE(queue.RemoveUpTo(25));
+  EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(25)));
   EXPECT_EQ(2u, queue.NumIntervals());
   EXPECT_EQ(10u, queue.LastIntervalLength());
-  EXPECT_EQ(25u, queue.Min());
-  EXPECT_EQ(49u, queue.Max());
+  EXPECT_EQ(QuicPacketNumber(25u), queue.Min());
+  EXPECT_EQ(QuicPacketNumber(49u), queue.Max());
 }
 
 }  // namespace
diff --git a/net/third_party/quic/core/frames/quic_stop_waiting_frame.cc b/net/third_party/quic/core/frames/quic_stop_waiting_frame.cc
index a89160f..f2486d5 100644
--- a/net/third_party/quic/core/frames/quic_stop_waiting_frame.cc
+++ b/net/third_party/quic/core/frames/quic_stop_waiting_frame.cc
@@ -8,7 +8,7 @@
 
 namespace quic {
 
-QuicStopWaitingFrame::QuicStopWaitingFrame() : least_unacked(0) {}
+QuicStopWaitingFrame::QuicStopWaitingFrame() {}
 
 QuicStopWaitingFrame::~QuicStopWaitingFrame() {}
 
diff --git a/net/third_party/quic/core/http/quic_server_session_base.cc b/net/third_party/quic/core/http/quic_server_session_base.cc
index 0a9fad0f..8d97ae241 100644
--- a/net/third_party/quic/core/http/quic_server_session_base.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base.cc
@@ -30,8 +30,7 @@
       helper_(helper),
       bandwidth_resumption_enabled_(false),
       bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()),
-      last_scup_time_(QuicTime::Zero()),
-      last_scup_packet_number_(0) {}
+      last_scup_time_(QuicTime::Zero()) {}
 
 QuicServerSessionBase::~QuicServerSessionBase() {}
 
@@ -112,9 +111,15 @@
   int64_t srtt_ms =
       sent_packet_manager.GetRttStats()->smoothed_rtt().ToMilliseconds();
   int64_t now_ms = (now - last_scup_time_).ToMilliseconds();
-  int64_t packets_since_last_scup =
-      connection()->sent_packet_manager().GetLargestSentPacket() -
-      last_scup_packet_number_;
+  int64_t packets_since_last_scup = 0;
+  const QuicPacketNumber largest_sent_packet =
+      connection()->sent_packet_manager().GetLargestSentPacket();
+  if (largest_sent_packet.IsInitialized()) {
+    packets_since_last_scup =
+        last_scup_packet_number_.IsInitialized()
+            ? largest_sent_packet - last_scup_packet_number_
+            : largest_sent_packet.ToUint64();
+  }
   if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) ||
       now_ms < kMinIntervalBetweenServerConfigUpdatesMs ||
       packets_since_last_scup < kMinPacketsBetweenServerConfigUpdates) {
diff --git a/net/third_party/quic/core/http/quic_server_session_base_test.cc b/net/third_party/quic/core/http/quic_server_session_base_test.cc
index 07520d37..238e04ab 100644
--- a/net/third_party/quic/core/http/quic_server_session_base_test.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base_test.cc
@@ -499,10 +499,11 @@
 
   // Bandwidth estimate has now changed sufficiently, enough time has passed,
   // and enough packets have been sent.
-  SerializedPacket packet(1 + kMinPacketsBetweenServerConfigUpdates,
-                          PACKET_4BYTE_PACKET_NUMBER, nullptr, 1000, false,
-                          false);
-  sent_packet_manager->OnPacketSent(&packet, 0, now, NOT_RETRANSMISSION,
+  SerializedPacket packet(
+      QuicPacketNumber(1) + kMinPacketsBetweenServerConfigUpdates,
+      PACKET_4BYTE_PACKET_NUMBER, nullptr, 1000, false, false);
+  sent_packet_manager->OnPacketSent(&packet, QuicPacketNumber(), now,
+                                    NOT_RETRANSMISSION,
                                     HAS_RETRANSMITTABLE_DATA);
 
   // Verify that the proto has exactly the values we expect.
diff --git a/net/third_party/quic/core/packet_number_indexed_queue.h b/net/third_party/quic/core/packet_number_indexed_queue.h
index 7b2b354c..afa1ccd 100644
--- a/net/third_party/quic/core/packet_number_indexed_queue.h
+++ b/net/third_party/quic/core/packet_number_indexed_queue.h
@@ -37,8 +37,7 @@
 template <typename T>
 class PacketNumberIndexedQueue {
  public:
-  PacketNumberIndexedQueue()
-      : number_of_present_entries_(0), first_packet_(kInvalidPacketNumber) {}
+  PacketNumberIndexedQueue() : number_of_present_entries_(0) {}
 
   // Retrieve the entry associated with the packet number.  Returns the pointer
   // to the entry in case of success, or nullptr if the entry does not exist.
@@ -75,7 +74,7 @@
   // empty.
   QuicPacketNumber last_packet() const {
     if (IsEmpty()) {
-      return kInvalidPacketNumber;
+      return QuicPacketNumber();
     }
     return first_packet_ + entries_.size() - 1;
   }
@@ -129,14 +128,14 @@
 template <typename... Args>
 bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number,
                                           Args&&... args) {
-  if (packet_number == kInvalidPacketNumber) {
+  if (!packet_number.IsInitialized()) {
     QUIC_BUG << "Try to insert an uninitialized packet number";
     return false;
   }
 
   if (IsEmpty()) {
     DCHECK(entries_.empty());
-    DCHECK_EQ(kInvalidPacketNumber, first_packet_);
+    DCHECK(!first_packet_.IsInitialized());
 
     entries_.emplace_back(std::forward<Args>(args)...);
     number_of_present_entries_ = 1;
@@ -183,14 +182,14 @@
     first_packet_++;
   }
   if (entries_.empty()) {
-    first_packet_ = kInvalidPacketNumber;
+    first_packet_.Clear();
   }
 }
 
 template <typename T>
 auto PacketNumberIndexedQueue<T>::GetEntryWrapper(
     QuicPacketNumber packet_number) const -> const EntryWrapper* {
-  if (packet_number == kInvalidPacketNumber || IsEmpty() ||
+  if (!packet_number.IsInitialized() || IsEmpty() ||
       packet_number < first_packet_) {
     return nullptr;
   }
diff --git a/net/third_party/quic/core/packet_number_indexed_queue_test.cc b/net/third_party/quic/core/packet_number_indexed_queue_test.cc
index 606617a..7993357 100644
--- a/net/third_party/quic/core/packet_number_indexed_queue_test.cc
+++ b/net/third_party/quic/core/packet_number_indexed_queue_test.cc
@@ -23,156 +23,156 @@
 
 TEST_F(PacketNumberIndexedQueueTest, InitialState) {
   EXPECT_TRUE(queue_.IsEmpty());
-  EXPECT_EQ(0u, queue_.first_packet());
-  EXPECT_EQ(0u, queue_.last_packet());
+  EXPECT_FALSE(queue_.first_packet().IsInitialized());
+  EXPECT_FALSE(queue_.last_packet().IsInitialized());
   EXPECT_EQ(0u, queue_.number_of_present_entries());
   EXPECT_EQ(0u, queue_.entry_slots_used());
 }
 
 TEST_F(PacketNumberIndexedQueueTest, InsertingContinuousElements) {
-  ASSERT_TRUE(queue_.Emplace(1001, "one"));
-  EXPECT_EQ("one", *queue_.GetEntry(1001));
+  ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1001), "one"));
+  EXPECT_EQ("one", *queue_.GetEntry(QuicPacketNumber(1001)));
 
-  ASSERT_TRUE(queue_.Emplace(1002, "two"));
-  EXPECT_EQ("two", *queue_.GetEntry(1002));
+  ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1002), "two"));
+  EXPECT_EQ("two", *queue_.GetEntry(QuicPacketNumber(1002)));
 
   EXPECT_FALSE(queue_.IsEmpty());
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(1002u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(1002u), queue_.last_packet());
   EXPECT_EQ(2u, queue_.number_of_present_entries());
   EXPECT_EQ(2u, queue_.entry_slots_used());
 }
 
 TEST_F(PacketNumberIndexedQueueTest, InsertingOutOfOrder) {
-  queue_.Emplace(1001, "one");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
 
-  ASSERT_TRUE(queue_.Emplace(1003, "three"));
-  EXPECT_EQ(nullptr, queue_.GetEntry(1002));
-  EXPECT_EQ("three", *queue_.GetEntry(1003));
+  ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1003), "three"));
+  EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002)));
+  EXPECT_EQ("three", *queue_.GetEntry(QuicPacketNumber(1003)));
 
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(1003u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
   EXPECT_EQ(2u, queue_.number_of_present_entries());
   EXPECT_EQ(3u, queue_.entry_slots_used());
 
-  ASSERT_FALSE(queue_.Emplace(1002, "two"));
+  ASSERT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two"));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, InsertingIntoPast) {
-  queue_.Emplace(1001, "one");
-  EXPECT_FALSE(queue_.Emplace(1000, "zero"));
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1000), "zero"));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, InsertingDuplicate) {
-  queue_.Emplace(1001, "one");
-  EXPECT_FALSE(queue_.Emplace(1001, "one"));
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1001), "one"));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, RemoveInTheMiddle) {
-  queue_.Emplace(1001, "one");
-  queue_.Emplace(1002, "two");
-  queue_.Emplace(1003, "three");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  queue_.Emplace(QuicPacketNumber(1002), "two");
+  queue_.Emplace(QuicPacketNumber(1003), "three");
 
-  ASSERT_TRUE(queue_.Remove(1002));
-  EXPECT_EQ(nullptr, queue_.GetEntry(1002));
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002)));
+  EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002)));
 
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(1003u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
   EXPECT_EQ(2u, queue_.number_of_present_entries());
   EXPECT_EQ(3u, queue_.entry_slots_used());
 
-  EXPECT_FALSE(queue_.Emplace(1002, "two"));
-  EXPECT_TRUE(queue_.Emplace(1004, "four"));
+  EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two"));
+  EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four"));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, RemoveAtImmediateEdges) {
-  queue_.Emplace(1001, "one");
-  queue_.Emplace(1002, "two");
-  queue_.Emplace(1003, "three");
-  ASSERT_TRUE(queue_.Remove(1001));
-  EXPECT_EQ(nullptr, queue_.GetEntry(1001));
-  ASSERT_TRUE(queue_.Remove(1003));
-  EXPECT_EQ(nullptr, queue_.GetEntry(1003));
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  queue_.Emplace(QuicPacketNumber(1002), "two");
+  queue_.Emplace(QuicPacketNumber(1003), "three");
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+  EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1001)));
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1003)));
+  EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1003)));
 
-  EXPECT_EQ(1002u, queue_.first_packet());
-  EXPECT_EQ(1003u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1002u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
   EXPECT_EQ(1u, queue_.number_of_present_entries());
   EXPECT_EQ(2u, queue_.entry_slots_used());
 
-  EXPECT_TRUE(queue_.Emplace(1004, "four"));
+  EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four"));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantFront) {
-  queue_.Emplace(1001, "one");
-  queue_.Emplace(1002, "one (kinda)");
-  queue_.Emplace(2001, "two");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  queue_.Emplace(QuicPacketNumber(1002), "one (kinda)");
+  queue_.Emplace(QuicPacketNumber(2001), "two");
 
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(2001u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
   EXPECT_EQ(3u, queue_.number_of_present_entries());
   EXPECT_EQ(1001u, queue_.entry_slots_used());
 
-  ASSERT_TRUE(queue_.Remove(1002));
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(2001u, queue_.last_packet());
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002)));
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
   EXPECT_EQ(2u, queue_.number_of_present_entries());
   EXPECT_EQ(1001u, queue_.entry_slots_used());
 
-  ASSERT_TRUE(queue_.Remove(1001));
-  EXPECT_EQ(2001u, queue_.first_packet());
-  EXPECT_EQ(2001u, queue_.last_packet());
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
   EXPECT_EQ(1u, queue_.number_of_present_entries());
   EXPECT_EQ(1u, queue_.entry_slots_used());
 }
 
 TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantBack) {
-  queue_.Emplace(1001, "one");
-  queue_.Emplace(2001, "two");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  queue_.Emplace(QuicPacketNumber(2001), "two");
 
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(2001u, queue_.last_packet());
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
 
-  ASSERT_TRUE(queue_.Remove(2001));
-  EXPECT_EQ(1001u, queue_.first_packet());
-  EXPECT_EQ(2001u, queue_.last_packet());
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001)));
+  EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
 }
 
 TEST_F(PacketNumberIndexedQueueTest, ClearAndRepopulate) {
-  queue_.Emplace(1001, "one");
-  queue_.Emplace(2001, "two");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  queue_.Emplace(QuicPacketNumber(2001), "two");
 
-  ASSERT_TRUE(queue_.Remove(1001));
-  ASSERT_TRUE(queue_.Remove(2001));
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001)));
   EXPECT_TRUE(queue_.IsEmpty());
-  EXPECT_EQ(0u, queue_.first_packet());
-  EXPECT_EQ(0u, queue_.last_packet());
+  EXPECT_FALSE(queue_.first_packet().IsInitialized());
+  EXPECT_FALSE(queue_.last_packet().IsInitialized());
 
-  EXPECT_TRUE(queue_.Emplace(101, "one"));
-  EXPECT_TRUE(queue_.Emplace(201, "two"));
-  EXPECT_EQ(101u, queue_.first_packet());
-  EXPECT_EQ(201u, queue_.last_packet());
+  EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(101), "one"));
+  EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(201), "two"));
+  EXPECT_EQ(QuicPacketNumber(101u), queue_.first_packet());
+  EXPECT_EQ(QuicPacketNumber(201u), queue_.last_packet());
 }
 
 TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsThatNeverExisted) {
-  ASSERT_FALSE(queue_.Remove(1000));
-  queue_.Emplace(1001, "one");
-  ASSERT_FALSE(queue_.Remove(1000));
-  ASSERT_FALSE(queue_.Remove(1002));
+  ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000)));
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000)));
+  ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1002)));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsTwice) {
-  queue_.Emplace(1001, "one");
-  ASSERT_TRUE(queue_.Remove(1001));
-  ASSERT_FALSE(queue_.Remove(1001));
-  ASSERT_FALSE(queue_.Remove(1001));
+  queue_.Emplace(QuicPacketNumber(1001), "one");
+  ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+  ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001)));
+  ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001)));
 }
 
 TEST_F(PacketNumberIndexedQueueTest, ConstGetter) {
-  queue_.Emplace(1001, "one");
+  queue_.Emplace(QuicPacketNumber(1001), "one");
   const auto& const_queue = queue_;
 
-  EXPECT_EQ("one", *const_queue.GetEntry(1001));
-  EXPECT_EQ(nullptr, const_queue.GetEntry(1002));
+  EXPECT_EQ("one", *const_queue.GetEntry(QuicPacketNumber(1001)));
+  EXPECT_EQ(nullptr, const_queue.GetEntry(QuicPacketNumber(1002)));
 }
 
 }  // namespace
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index 3c261c19..7b0fc5a8 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -245,15 +245,12 @@
       peer_address_(initial_peer_address),
       direct_peer_address_(initial_peer_address),
       active_effective_peer_migration_type_(NO_CHANGE),
-      highest_packet_sent_before_effective_peer_migration_(0),
       last_packet_decrypted_(false),
       last_size_(0),
       current_packet_data_(nullptr),
       last_decrypted_packet_level_(ENCRYPTION_NONE),
       should_last_packet_instigate_acks_(false),
       was_last_packet_missing_(false),
-      largest_seen_packet_with_ack_(0),
-      largest_seen_packet_with_stop_waiting_(0),
       max_undecryptable_packets_(0),
       max_tracked_packets_(kMaxTrackedPackets),
       pending_version_negotiation_packet_(false),
@@ -784,7 +781,7 @@
       // Packets should have the version flag till version negotiation is
       // done.
       QuicString error_details =
-          QuicStrCat(ENDPOINT, "Packet ", header.packet_number,
+          QuicStrCat(ENDPOINT, "Packet ", header.packet_number.ToUint64(),
                      " without version flag before version negotiated.");
       QUIC_DLOG(WARNING) << error_details;
       CloseConnection(QUIC_INVALID_VERSION, error_details,
@@ -842,7 +839,8 @@
   current_effective_peer_migration_type_ = NO_CHANGE;
 
   if (perspective_ == Perspective::IS_CLIENT) {
-    if (header.packet_number > received_packet_manager_.GetLargestObserved()) {
+    if (!received_packet_manager_.GetLargestObserved().IsInitialized() ||
+        header.packet_number > received_packet_manager_.GetLargestObserved()) {
       // Update peer_address_ and effective_peer_address_ immediately for
       // client connections.
       direct_peer_address_ = last_packet_source_address_;
@@ -946,22 +944,25 @@
   QUIC_DVLOG(1) << ENDPOINT
                 << "OnAckFrameStart, largest_acked: " << largest_acked;
 
-  if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
+  if (largest_seen_packet_with_ack_.IsInitialized() &&
+      last_header_.packet_number <= largest_seen_packet_with_ack_) {
     QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
     return true;
   }
 
-  if (largest_acked > sent_packet_manager_.GetLargestSentPacket()) {
+  if (!sent_packet_manager_.GetLargestSentPacket().IsInitialized() ||
+      largest_acked > sent_packet_manager_.GetLargestSentPacket()) {
     QUIC_DLOG(WARNING) << ENDPOINT
                        << "Peer's observed unsent packet:" << largest_acked
                        << " vs " << sent_packet_manager_.GetLargestSentPacket();
-    // We got an error for data we have not sent.
+    // We got an ack for data we have not sent.
     CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too high.",
                     ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
     return false;
   }
 
-  if (largest_acked > sent_packet_manager_.GetLargestObserved()) {
+  if (!sent_packet_manager_.GetLargestObserved().IsInitialized() ||
+      largest_acked > sent_packet_manager_.GetLargestObserved()) {
     visitor_->OnForwardProgressConfirmed();
   } else if (largest_acked < sent_packet_manager_.GetLargestObserved()) {
     QUIC_LOG(INFO) << ENDPOINT << "Peer's largest_observed packet decreased:"
@@ -986,7 +987,8 @@
   DCHECK(connected_);
   QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")";
 
-  if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
+  if (largest_seen_packet_with_ack_.IsInitialized() &&
+      last_header_.packet_number <= largest_seen_packet_with_ack_) {
     QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
     return true;
   }
@@ -1001,7 +1003,8 @@
   QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", "
                 << timestamp.ToDebuggingValue() << ")";
 
-  if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
+  if (largest_seen_packet_with_ack_.IsInitialized() &&
+      last_header_.packet_number <= largest_seen_packet_with_ack_) {
     QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
     return true;
   }
@@ -1014,7 +1017,8 @@
   DCHECK(connected_);
   QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start;
 
-  if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
+  if (largest_seen_packet_with_ack_.IsInitialized() &&
+      last_header_.packet_number <= largest_seen_packet_with_ack_) {
     QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
     return true;
   }
@@ -1053,7 +1057,8 @@
   if (no_stop_waiting_frames_) {
     return true;
   }
-  if (last_header_.packet_number <= largest_seen_packet_with_stop_waiting_) {
+  if (largest_seen_packet_with_stop_waiting_.IsInitialized() &&
+      last_header_.packet_number <= largest_seen_packet_with_stop_waiting_) {
     QUIC_DLOG(INFO) << ENDPOINT
                     << "Received an old stop waiting frame: ignoring";
     return true;
@@ -1131,8 +1136,10 @@
 
 const char* QuicConnection::ValidateStopWaitingFrame(
     const QuicStopWaitingFrame& stop_waiting) {
-  if (stop_waiting.least_unacked <
-      received_packet_manager_.peer_least_packet_awaiting_ack()) {
+  if (received_packet_manager_.peer_least_packet_awaiting_ack()
+          .IsInitialized() &&
+      stop_waiting.least_unacked <
+          received_packet_manager_.peer_least_packet_awaiting_ack()) {
     QUIC_DLOG(ERROR)
         << ENDPOINT
         << "Peer's sent low least_unacked: " << stop_waiting.least_unacked
@@ -1441,8 +1448,10 @@
   if (was_missing) {
     // Only ack immediately if an ACK frame was sent with a larger
     // largest acked than the newly received packet number.
-    if (last_header_.packet_number <
-        sent_packet_manager_.unacked_packets().largest_sent_largest_acked()) {
+    const QuicPacketNumber largest_sent_largest_acked =
+        sent_packet_manager_.unacked_packets().largest_sent_largest_acked();
+    if (largest_sent_largest_acked.IsInitialized() &&
+        last_header_.packet_number < largest_sent_largest_acked) {
       ack_queued_ = true;
     }
   }
@@ -1450,7 +1459,10 @@
   if (should_last_packet_instigate_acks_ && !ack_queued_) {
     ++num_retransmittable_packets_received_since_last_ack_sent_;
     if (ack_mode_ != TCP_ACKING &&
-        last_header_.packet_number > min_received_before_ack_decimation_) {
+        // TODO(fayang): Fix this as this check assumes the first received
+        // packet is 1.
+        last_header_.packet_number >
+            QuicPacketNumber(min_received_before_ack_decimation_)) {
       // Ack up to 10 packets at once unless ack decimation is unlimited.
       if (!unlimited_ack_decimation_ &&
           num_retransmittable_packets_received_since_last_ack_sent_ >=
@@ -1530,8 +1542,9 @@
 void QuicConnection::CloseIfTooManyOutstandingSentPackets() {
   // This occurs if we don't discard old packets we've seen fast enough. It's
   // possible largest observed is less than leaset unacked.
-  if (sent_packet_manager_.GetLargestObserved() >
-      sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_) {
+  if (sent_packet_manager_.GetLargestObserved().IsInitialized() &&
+      sent_packet_manager_.GetLargestObserved() >
+          sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_) {
     CloseConnection(
         QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
         QuicStrCat("More than ", max_tracked_packets_, " outstanding."),
@@ -1790,8 +1803,10 @@
       << ", highest_packet_sent_before_effective_peer_migration_ = "
       << highest_packet_sent_before_effective_peer_migration_;
   if (active_effective_peer_migration_type_ != NO_CHANGE &&
-      sent_packet_manager_.GetLargestObserved() >
-          highest_packet_sent_before_effective_peer_migration_) {
+      sent_packet_manager_.GetLargestObserved().IsInitialized() &&
+      (!highest_packet_sent_before_effective_peer_migration_.IsInitialized() ||
+       sent_packet_manager_.GetLargestObserved() >
+           highest_packet_sent_before_effective_peer_migration_)) {
     if (perspective_ == Perspective::IS_SERVER) {
       OnEffectivePeerMigrationValidated();
     }
@@ -1886,7 +1901,7 @@
     QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 2, 2);
     // Configured to accept any packet number in range 1...0x7fffffff
     // as initial packet number.
-    if (last_header_.packet_number != kInvalidPacketNumber) {
+    if (last_header_.packet_number.IsInitialized()) {
       // The last packet's number is not 0. Ensure that this packet
       // is reasonably close to where it should be.
       if (!Near(header.packet_number, last_header_.packet_number)) {
@@ -1899,10 +1914,9 @@
       }
     } else {
       // The "last packet's number" is 0, meaning that this packet is the first
-      // one received. Ensure it is in range 1..kMaxRandomInitialPacketNumber,
+      // one received. Ensure it is in range 1..MaxRandomInitialPacketNumber(),
       // inclusive.
-      if ((header.packet_number == kInvalidPacketNumber) ||
-          (header.packet_number > kMaxRandomInitialPacketNumber)) {
+      if ((header.packet_number > MaxRandomInitialPacketNumber())) {
         // packet number is bad.
         QUIC_DLOG(INFO) << ENDPOINT << "Initial packet " << header.packet_number
                         << " out of bounds.  Discarding";
@@ -1916,21 +1930,29 @@
     // Count those that would have been accepted if FLAGS..random_ipn
     // were true -- to detect/diagnose potential issues prior to
     // enabling the flag.
-    if ((header.packet_number > 1) &&
-        (header.packet_number <= kMaxRandomInitialPacketNumber)) {
+    if ((header.packet_number > QuicPacketNumber(1)) &&
+        (header.packet_number <= MaxRandomInitialPacketNumber())) {
       QUIC_CODE_COUNT_N(had_possibly_random_ipn, 2, 2);
     }
-
-    if (!Near(header.packet_number, last_header_.packet_number)) {
+    bool out_of_bound =
+        last_header_.packet_number.IsInitialized()
+            ? !Near(header.packet_number, last_header_.packet_number)
+            // TODO(fayang): Fix this as this check assume the first received
+            // packet is 1.
+            : header.packet_number > QuicPacketNumber(kMaxPacketGap);
+    if (out_of_bound) {
       QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number
                       << " out of bounds.  Discarding";
       QuicStringPiece packet_data = GetCurrentPacket();
       const size_t kMaxPacketLengthInErrorDetails = 64;
       CloseConnection(
           QUIC_INVALID_PACKET_HEADER,
-          QuicStrCat("Packet number out of bounds. last_pkn=",
-                     last_header_.packet_number,
-                     ", current_pkn=", header.packet_number,
+          QuicStrCat("Packet number out of bounds. ",
+                     last_header_.packet_number.IsInitialized()
+                         ? QuicStrCat("last_pkn=",
+                                      last_header_.packet_number.ToUint64())
+                         : "first received packet",
+                     ", current_pkn=", header.packet_number.ToUint64(),
                      ", current_pkt_len=", packet_data.length(),
                      ", current_hdr=",
                      QuicTextUtils::HexEncode(
@@ -2132,7 +2154,8 @@
     ++stats_.packets_discarded;
     return true;
   }
-  if (packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) {
+  if (sent_packet_manager_.GetLargestSentPacket().IsInitialized() &&
+      packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) {
     QUIC_BUG << "Attempt to write packet:" << packet->packet_number
              << " after:" << sent_packet_manager_.GetLargestSentPacket();
     QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsAtOutOfOrder",
@@ -2420,7 +2443,7 @@
 
   if (transport_version() != QUIC_VERSION_35) {
     if (serialized_packet->retransmittable_frames.empty() &&
-        serialized_packet->original_packet_number == kInvalidPacketNumber) {
+        !serialized_packet->original_packet_number.IsInitialized()) {
       // Increment consecutive_num_packets_with_no_retransmittable_frames_ if
       // this packet is a new transmission with no retransmittable frames.
       ++consecutive_num_packets_with_no_retransmittable_frames_;
@@ -3237,7 +3260,7 @@
     QUIC_BUG << "No migration underway.";
     return;
   }
-  highest_packet_sent_before_effective_peer_migration_ = 0;
+  highest_packet_sent_before_effective_peer_migration_.Clear();
   active_effective_peer_migration_type_ = NO_CHANGE;
 }
 
@@ -3391,8 +3414,9 @@
   }
 
   current_packet_content_ = NOT_PADDED_PING;
-  if (last_header_.packet_number ==
-      received_packet_manager_.GetLargestObserved()) {
+  if (received_packet_manager_.GetLargestObserved().IsInitialized() &&
+      last_header_.packet_number ==
+          received_packet_manager_.GetLargestObserved()) {
     direct_peer_address_ = last_packet_source_address_;
     if (current_effective_peer_migration_type_ != NO_CHANGE) {
       // Start effective peer migration immediately when the current packet is
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc
index 29d1001..bead8334 100644
--- a/net/third_party/quic/core/quic_connection_test.cc
+++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -583,10 +583,11 @@
     char buffer[kMaxPacketSize];
     size_t encrypted_length =
         QuicConnectionPeer::GetFramer(this)->EncryptPayload(
-            ENCRYPTION_NONE, packet_number, *packet, buffer, kMaxPacketSize);
+            ENCRYPTION_NONE, QuicPacketNumber(packet_number), *packet, buffer,
+            kMaxPacketSize);
     SerializedPacket serialized_packet(
-        packet_number, PACKET_4BYTE_PACKET_NUMBER, buffer, encrypted_length,
-        has_ack, has_pending_frames);
+        QuicPacketNumber(packet_number), PACKET_4BYTE_PACKET_NUMBER, buffer,
+        encrypted_length, has_ack, has_pending_frames);
     if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
       serialized_packet.retransmittable_frames.push_back(
           QuicFrame(QuicStreamFrame()));
@@ -923,7 +924,7 @@
 
   QuicPacketNumber least_unacked() {
     if (writer_->stop_waiting_frames().empty()) {
-      return 0;
+      return QuicPacketNumber();
     }
     return writer_->stop_waiting_frames()[0].least_unacked;
   }
@@ -1016,14 +1017,14 @@
         peer_framer_.perspective() == Perspective::IS_SERVER) {
       header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
     }
-    header.packet_number = number;
+    header.packet_number = QuicPacketNumber(number);
     QuicFrames frames;
     frames.push_back(frame);
     std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
 
     char buffer[kMaxPacketSize];
-    size_t encrypted_length =
-        framer_.EncryptPayload(level, number, *packet, buffer, kMaxPacketSize);
+    size_t encrypted_length = framer_.EncryptPayload(
+        level, QuicPacketNumber(number), *packet, buffer, kMaxPacketSize);
     connection_.ProcessUdpPacket(
         kSelfAddress, kPeerAddress,
         QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
@@ -1034,6 +1035,17 @@
     return ProcessDataPacketAtLevel(number, false, ENCRYPTION_NONE);
   }
 
+  size_t ProcessDataPacket(QuicPacketNumber packet_number) {
+    return ProcessDataPacketAtLevel(packet_number, false, ENCRYPTION_NONE);
+  }
+
+  size_t ProcessDataPacketAtLevel(QuicPacketNumber packet_number,
+                                  bool has_stop_waiting,
+                                  EncryptionLevel level) {
+    return ProcessDataPacketAtLevel(packet_number.ToUint64(), has_stop_waiting,
+                                    level);
+  }
+
   size_t ProcessDataPacketAtLevel(uint64_t number,
                                   bool has_stop_waiting,
                                   EncryptionLevel level) {
@@ -1041,7 +1053,7 @@
         ConstructDataPacket(number, has_stop_waiting));
     char buffer[kMaxPacketSize];
     size_t encrypted_length = peer_framer_.EncryptPayload(
-        level, number, *packet, buffer, kMaxPacketSize);
+        level, QuicPacketNumber(number), *packet, buffer, kMaxPacketSize);
     connection_.ProcessUdpPacket(
         kSelfAddress, kPeerAddress,
         QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
@@ -1054,8 +1066,9 @@
   void ProcessClosePacket(uint64_t number) {
     std::unique_ptr<QuicPacket> packet(ConstructClosePacket(number));
     char buffer[kMaxPacketSize];
-    size_t encrypted_length = peer_framer_.EncryptPayload(
-        ENCRYPTION_NONE, number, *packet, buffer, kMaxPacketSize);
+    size_t encrypted_length =
+        peer_framer_.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(number),
+                                    *packet, buffer, kMaxPacketSize);
     connection_.ProcessUdpPacket(
         kSelfAddress, kPeerAddress,
         QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
@@ -1106,7 +1119,11 @@
   }
 
   void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) {
-    QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1);
+    if (packet_number > 1) {
+      QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1);
+    } else {
+      QuicPacketCreatorPeer::ClearPacketNumber(&peer_creator_);
+    }
     ProcessFramePacket(QuicFrame(frame));
   }
 
@@ -1130,7 +1147,8 @@
   }
 
   bool IsMissing(uint64_t number) {
-    return IsAwaitingPacket(*outgoing_ack(), number, 0);
+    return IsAwaitingPacket(*outgoing_ack(), QuicPacketNumber(number),
+                            QuicPacketNumber());
   }
 
   std::unique_ptr<QuicPacket> ConstructPacket(const QuicPacketHeader& header,
@@ -1152,7 +1170,7 @@
         peer_framer_.perspective() == Perspective::IS_SERVER) {
       header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
     }
-    header.packet_number = number;
+    header.packet_number = QuicPacketNumber(number);
 
     QuicFrames frames;
     frames.push_back(QuicFrame(frame1_));
@@ -1179,7 +1197,7 @@
     // Set connection_id to peer's in memory representation as this connection
     // close packet is created by peer_framer.
     header.destination_connection_id = connection_id_;
-    header.packet_number = number;
+    header.packet_number = QuicPacketNumber(number);
     if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
         peer_framer_.perspective() == Perspective::IS_SERVER) {
       header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
@@ -1203,7 +1221,7 @@
 
   const QuicStopWaitingFrame InitStopWaitingFrame(uint64_t least_unacked) {
     QuicStopWaitingFrame frame;
-    frame.least_unacked = least_unacked;
+    frame.least_unacked = QuicPacketNumber(least_unacked);
     return frame;
   }
 
@@ -1211,10 +1229,17 @@
   // |largest_acked|, except |missing|.
   // REQUIRES: 1 <= |missing| < |largest_acked|
   QuicAckFrame ConstructAckFrame(uint64_t largest_acked, uint64_t missing) {
-    if (missing == 1) {
+    return ConstructAckFrame(QuicPacketNumber(largest_acked),
+                             QuicPacketNumber(missing));
+  }
+
+  QuicAckFrame ConstructAckFrame(QuicPacketNumber largest_acked,
+                                 QuicPacketNumber missing) {
+    if (missing == QuicPacketNumber(1)) {
       return InitAckFrame({{missing + 1, largest_acked + 1}});
     }
-    return InitAckFrame({{1, missing}, {missing + 1, largest_acked + 1}});
+    return InitAckFrame(
+        {{QuicPacketNumber(1), missing}, {missing + 1, largest_acked + 1}});
   }
 
   // Undo nacking a packet within the frame.
@@ -1230,7 +1255,7 @@
     // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
     // packet call to the visitor.
     if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
-      ProcessDataPacket(kMaxRandomInitialPacketNumber + 6000);
+      ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000);
     } else {
       ProcessDataPacket(6000);
     }
@@ -1275,8 +1300,8 @@
       const QuicPacketCount packets_between_probes_base) {
     QuicConnectionPeer::SetPacketsBetweenMtuProbes(&connection_,
                                                    packets_between_probes_base);
-    QuicConnectionPeer::SetNextMtuProbeAt(&connection_,
-                                          packets_between_probes_base);
+    QuicConnectionPeer::SetNextMtuProbeAt(
+        &connection_, QuicPacketNumber(packets_between_probes_base));
   }
 
   bool IsDefaultTestConfiguration() {
@@ -1942,7 +1967,7 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.version_flag = true;
-  header.packet_number = 1;
+  header.packet_number = QuicPacketNumber(1);
 
   QuicFrames frames;
   QuicPaddingFrame padding;
@@ -1951,7 +1976,7 @@
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = peer_framer_.EncryptPayload(
-      ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize);
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
   EXPECT_EQ(kMaxPacketSize, encrypted_length);
 
   framer_.set_version(version());
@@ -1975,7 +2000,7 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.version_flag = true;
-  header.packet_number = 1;
+  header.packet_number = QuicPacketNumber(1);
 
   QuicFrames frames;
   QuicPaddingFrame padding;
@@ -1984,7 +2009,7 @@
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = peer_framer_.EncryptPayload(
-      ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize);
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
   EXPECT_EQ(kMaxPacketSize, encrypted_length);
 
   framer_.set_version(version());
@@ -2024,15 +2049,15 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(1);
-  EXPECT_EQ(1u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(1u), LargestAcked(*outgoing_ack()));
   EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
 
   ProcessPacket(2);
-  EXPECT_EQ(2u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(*outgoing_ack()));
   EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
 
   ProcessPacket(3);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
 }
 
@@ -2040,17 +2065,17 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(3);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(2));
   EXPECT_TRUE(IsMissing(1));
 
   ProcessPacket(2);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_FALSE(IsMissing(2));
   EXPECT_TRUE(IsMissing(1));
 
   ProcessPacket(1);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_FALSE(IsMissing(2));
   EXPECT_FALSE(IsMissing(1));
 }
@@ -2059,14 +2084,14 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(3);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(2));
   EXPECT_TRUE(IsMissing(1));
 
   // Send packet 3 again, but do not set the expectation that
   // the visitor OnStreamFrame() will be called.
   ProcessDataPacket(3);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(2));
   EXPECT_TRUE(IsMissing(1));
 }
@@ -2075,16 +2100,16 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(3);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(2));
   EXPECT_TRUE(IsMissing(1));
 
   ProcessPacket(2);
-  EXPECT_EQ(3u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(1));
 
   ProcessPacket(5);
-  EXPECT_EQ(5u, LargestAcked(*outgoing_ack()));
+  EXPECT_EQ(QuicPacketNumber(5u), LargestAcked(*outgoing_ack()));
   EXPECT_TRUE(IsMissing(1));
   EXPECT_TRUE(IsMissing(4));
 
@@ -2108,7 +2133,7 @@
   // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
   // packet call to the visitor.
   if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
-    ProcessDataPacket(kMaxRandomInitialPacketNumber + 6000);
+    ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000);
   } else {
     ProcessDataPacket(6000);
   }
@@ -2458,36 +2483,36 @@
 TEST_P(QuicConnectionTest, BasicSending) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
-  ProcessDataPacket(QuicPacketNumber(1));
-  QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, QuicPacketNumber(2));
+  ProcessDataPacket(1);
+  QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2);
   QuicPacketNumber last_packet;
   SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);  // Packet 1
-  EXPECT_EQ(1u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(1u), last_packet);
   SendAckPacketToPeer();  // Packet 2
 
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(1u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
   }
 
   SendAckPacketToPeer();  // Packet 3
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(1u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
   }
 
   SendStreamDataToPeer(1, "bar", 3, NO_FIN, &last_packet);  // Packet 4
-  EXPECT_EQ(4u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(4u), last_packet);
   SendAckPacketToPeer();  // Packet 5
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(1u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
   }
 
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -2501,9 +2526,9 @@
   // ack for 4.
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(4u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
   }
 
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -2513,34 +2538,34 @@
   ProcessAckPacket(&frame2);  // Acks don't instigate acks.
 
   // Verify that we did not send an ack.
-  EXPECT_EQ(6u, writer_->header().packet_number);
+  EXPECT_EQ(QuicPacketNumber(6u), writer_->header().packet_number);
 
   // So the last ack has not changed.
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(4u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
   }
 
   // If we force an ack, we shouldn't change our retransmit state.
   SendAckPacketToPeer();  // Packet 7
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(7u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(7u), least_unacked());
   }
 
   // But if we send more data it should.
   SendStreamDataToPeer(1, "eep", 6, NO_FIN, &last_packet);  // Packet 8
-  EXPECT_EQ(8u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(8u), last_packet);
   SendAckPacketToPeer();  // Packet 9
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(7u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(7u), least_unacked());
   }
 }
 
@@ -2857,7 +2882,7 @@
   // Lose a packet and ensure it triggers retransmission.
   QuicAckFrame nack_two = ConstructAckFrame(3, 2);
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(2, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(2), kMaxPacketSize));
   EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
       .WillOnce(SetArgPointee<5>(lost_packets));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -3106,7 +3131,7 @@
   BlockOnNextWrite();
 
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(2, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(2), kMaxPacketSize));
   EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
       .WillOnce(SetArgPointee<5>(lost_packets));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -3121,7 +3146,8 @@
 
   // Unblock the socket and attempt to send the queued packets. We will always
   // send the retransmission.
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _)).Times(1);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(4), _, _))
+      .Times(1);
 
   writer_->SetWritable();
   connection_.OnCanWrite();
@@ -3278,14 +3304,15 @@
   // Process packet number 1. Can not call ProcessPacket or ProcessDataPacket
   // here, because they will fire the alarm after QuicConnection::ProcessPacket
   // is returned.
-  const QuicPacketNumber received_packet_num = 1;
+  const uint64_t received_packet_num = 1;
   const bool has_stop_waiting = false;
   const EncryptionLevel level = ENCRYPTION_NONE;
   std::unique_ptr<QuicPacket> packet(
       ConstructDataPacket(received_packet_num, has_stop_waiting));
   char buffer[kMaxPacketSize];
-  size_t encrypted_length = peer_framer_.EncryptPayload(
-      level, received_packet_num, *packet, buffer, kMaxPacketSize);
+  size_t encrypted_length =
+      peer_framer_.EncryptPayload(level, QuicPacketNumber(received_packet_num),
+                                  *packet, buffer, kMaxPacketSize);
   connection_.ProcessUdpPacket(
       kSelfAddress, kPeerAddress,
       QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
@@ -3355,12 +3382,13 @@
 
   // Ack 15, nack 1-14.
 
-  QuicAckFrame nack = InitAckFrame({{15, 16}});
+  QuicAckFrame nack =
+      InitAckFrame({{QuicPacketNumber(15), QuicPacketNumber(16)}});
 
   // 14 packets have been NACK'd and lost.
   LostPacketVector lost_packets;
   for (int i = 1; i < 15; ++i) {
-    lost_packets.push_back(LostPacket(i, kMaxPacketSize));
+    lost_packets.push_back(LostPacket(QuicPacketNumber(i), kMaxPacketSize));
   }
   EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
       .WillOnce(SetArgPointee<5>(lost_packets));
@@ -3381,16 +3409,16 @@
   QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2);
   QuicPacketNumber last_packet;
   SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);  // Packet 1
-  EXPECT_EQ(1u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(1u), last_packet);
   SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet);  // Packet 2
-  EXPECT_EQ(2u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(2u), last_packet);
   SendAckPacketToPeer();                                    // Packet 3
   SendStreamDataToPeer(5, "foo", 0, NO_FIN, &last_packet);  // Packet 4
-  EXPECT_EQ(4u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(4u), last_packet);
   SendStreamDataToPeer(1, "foo", 3, NO_FIN, &last_packet);  // Packet 5
-  EXPECT_EQ(5u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(5u), last_packet);
   SendStreamDataToPeer(3, "foo", 3, NO_FIN, &last_packet);  // Packet 6
-  EXPECT_EQ(6u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(6u), last_packet);
 
   // Client will ack packets 1, 2, [!3], 4, 5.
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -3419,25 +3447,25 @@
   // Verify that our internal state has least-unacked as 2, because we're still
   // waiting for a potential ack for 2.
 
-  EXPECT_EQ(2u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(2u), stop_waiting()->least_unacked);
 
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
   frame = InitAckFrame(2);
   ProcessAckPacket(&frame);
-  EXPECT_EQ(3u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked);
 
   // When we send an ack, we make sure our least-unacked makes sense.  In this
   // case since we're not waiting on an ack for 2 and all packets are acked, we
   // set it to 3.
   SendAckPacketToPeer();  // Packet 3
   // Least_unacked remains at 3 until another ack is received.
-  EXPECT_EQ(3u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked);
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
     // Check that the outgoing ack had its packet number as least_unacked.
-    EXPECT_EQ(3u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(3u), least_unacked());
   }
 
   // Ack the ack, which updates the rtt and raises the least unacked.
@@ -3446,13 +3474,13 @@
   ProcessAckPacket(&frame);
 
   SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr);  // Packet 4
-  EXPECT_EQ(4u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(4u), stop_waiting()->least_unacked);
   SendAckPacketToPeer();  // Packet 5
   if (GetParam().no_stop_waiting) {
     // Expect no stop waiting frame is sent.
-    EXPECT_EQ(0u, least_unacked());
+    EXPECT_FALSE(least_unacked().IsInitialized());
   } else {
-    EXPECT_EQ(4u, least_unacked());
+    EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
   }
 
   // Send two data packets at the end, and ensure if the last one is acked,
@@ -3461,30 +3489,31 @@
   SendStreamDataToPeer(1, "bar", 9, NO_FIN, nullptr);  // Packet 7
 
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  frame = InitAckFrame({{1, 5}, {7, 8}});
+  frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)},
+                        {QuicPacketNumber(7), QuicPacketNumber(8)}});
   ProcessAckPacket(&frame);
 
-  EXPECT_EQ(6u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(6u), stop_waiting()->least_unacked);
 }
 
 TEST_P(QuicConnectionTest, TLP) {
   connection_.SetMaxTailLossProbes(1);
 
   SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
-  EXPECT_EQ(1u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
   QuicTime retransmission_time =
       connection_.GetRetransmissionAlarm()->deadline();
   EXPECT_NE(QuicTime::Zero(), retransmission_time);
 
-  EXPECT_EQ(1u, writer_->header().packet_number);
+  EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number);
   // Simulate the retransmission alarm firing and sending a tlp,
   // so send algorithm's OnRetransmissionTimeout is not called.
   clock_.AdvanceTime(retransmission_time - clock_.Now());
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
   connection_.GetRetransmissionAlarm()->Fire();
-  EXPECT_EQ(2u, writer_->header().packet_number);
+  EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number);
   // We do not raise the high water mark yet.
-  EXPECT_EQ(1u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
 }
 
 TEST_P(QuicConnectionTest, RTO) {
@@ -3493,18 +3522,18 @@
   QuicTime default_retransmission_time =
       clock_.ApproximateNow() + DefaultRetransmissionTime();
   SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
-  EXPECT_EQ(1u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
 
-  EXPECT_EQ(1u, writer_->header().packet_number);
+  EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number);
   EXPECT_EQ(default_retransmission_time,
             connection_.GetRetransmissionAlarm()->deadline());
   // Simulate the retransmission alarm firing.
   clock_.AdvanceTime(DefaultRetransmissionTime());
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
   connection_.GetRetransmissionAlarm()->Fire();
-  EXPECT_EQ(2u, writer_->header().packet_number);
+  EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number);
   // We do not raise the high water mark yet.
-  EXPECT_EQ(1u, stop_waiting()->least_unacked);
+  EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
 }
 
 TEST_P(QuicConnectionTest, RetransmitWithSameEncryptionLevel) {
@@ -3527,8 +3556,10 @@
 
   {
     InSequence s;
-    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 3, _, _));
-    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _));
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, QuicPacketNumber(3), _, _));
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, _, QuicPacketNumber(4), _, _));
   }
 
   // Manually mark both packets for retransmission.
@@ -3700,7 +3731,7 @@
 
   // Process an encrypted packet which can not yet be decrypted which should
   // result in the packet being buffered.
-  for (QuicPacketNumber i = 1; i <= 100; ++i) {
+  for (uint64_t i = 1; i <= 100; ++i) {
     ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_INITIAL);
   }
 
@@ -3842,7 +3873,7 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20));
   QuicPacketNumber last_packet;
   SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
-  EXPECT_EQ(1u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(1u), last_packet);
   // This will be the updated deadline for the connection to idle time out.
   QuicTime new_ddl = clock_.ApproximateNow() +
                      QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
@@ -3894,12 +3925,12 @@
   // execution until manually adjusted.
   QuicPacketNumber last_packet;
   SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
-  EXPECT_EQ(1u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(1u), last_packet);
 
   // Advance the time and send the second packet to the peer.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
   SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
-  EXPECT_EQ(2u, last_packet);
+  EXPECT_EQ(QuicPacketNumber(2u), last_packet);
 
   if (GetQuicReloadableFlag(
           quic_fix_time_of_first_packet_sent_after_receiving)) {
@@ -4086,7 +4117,7 @@
       .WillOnce(SaveArg<3>(&mtu_probe_size));
   connection_.SendMtuDiscoveryPacket(new_mtu);
   EXPECT_EQ(new_mtu, mtu_probe_size);
-  EXPECT_EQ(1u, creator_->packet_number());
+  EXPECT_EQ(QuicPacketNumber(1u), creator_->packet_number());
 
   // Send more than MTU worth of data.  No acknowledgement was received so far,
   // so the MTU should be at its old value.
@@ -4097,7 +4128,7 @@
       .WillOnce(SaveArg<3>(&size_before_mtu_change))
       .WillOnce(Return());
   connection_.SendStreamDataWithString(3, data, 0, FIN);
-  EXPECT_EQ(3u, creator_->packet_number());
+  EXPECT_EQ(QuicPacketNumber(3u), creator_->packet_number());
   EXPECT_EQ(kDefaultMaxPacketSize, size_before_mtu_change);
 
   // Acknowledge all packets so far.
@@ -4110,7 +4141,7 @@
   // Send the same data again.  Check that it fits into a single packet now.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   connection_.SendStreamDataWithString(3, data, 0, FIN);
-  EXPECT_EQ(4u, creator_->packet_number());
+  EXPECT_EQ(QuicPacketNumber(4u), creator_->packet_number());
 }
 
 // Tests whether MTU discovery does not happen when it is not explicitly enabled
@@ -4156,7 +4187,7 @@
   EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size);
 
   const QuicPacketNumber probe_packet_number =
-      kFirstSendingPacketNumber + packets_between_probes_base;
+      FirstSendingPacketNumber() + packets_between_probes_base;
   ASSERT_EQ(probe_packet_number, creator_->packet_number());
 
   // Acknowledge all packets sent so far.
@@ -4218,12 +4249,13 @@
                                                  mtu_discovery_packets.end());
       QuicPacketNumber max_packet = *max_element(mtu_discovery_packets.begin(),
                                                  mtu_discovery_packets.end());
-      ack.packets.AddRange(1, min_packet);
-      ack.packets.AddRange(max_packet + 1, creator_->packet_number() + 1);
+      ack.packets.AddRange(QuicPacketNumber(1), min_packet);
+      ack.packets.AddRange(QuicPacketNumber(max_packet + 1),
+                           creator_->packet_number() + 1);
       ack.largest_acked = creator_->packet_number();
 
     } else {
-      ack.packets.AddRange(1, creator_->packet_number() + 1);
+      ack.packets.AddRange(QuicPacketNumber(1), creator_->packet_number() + 1);
       ack.largest_acked = creator_->packet_number();
     }
 
@@ -4245,11 +4277,12 @@
   // Ensure the number of packets between probes grows exponentially by checking
   // it against the closed-form expression for the packet number.
   ASSERT_EQ(kMtuDiscoveryAttempts, mtu_discovery_packets.size());
-  for (QuicPacketNumber i = 0; i < kMtuDiscoveryAttempts; i++) {
+  for (uint64_t i = 0; i < kMtuDiscoveryAttempts; i++) {
     // 2^0 + 2^1 + 2^2 + ... + 2^n = 2^(n + 1) - 1
     const QuicPacketCount packets_between_probes =
         packets_between_probes_base * ((1 << (i + 1)) - 1);
-    EXPECT_EQ(packets_between_probes + (i + 1), mtu_discovery_packets[i]);
+    EXPECT_EQ(QuicPacketNumber(packets_between_probes + (i + 1)),
+              mtu_discovery_packets[i]);
   }
 
   EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
@@ -4286,7 +4319,7 @@
   EXPECT_EQ(mtu_limit, probe_size);
 
   const QuicPacketNumber probe_sequence_number =
-      kFirstSendingPacketNumber + packets_between_probes_base;
+      FirstSendingPacketNumber() + packets_between_probes_base;
   ASSERT_EQ(probe_sequence_number, creator_->packet_number());
 
   // Acknowledge all packets sent so far.
@@ -4481,7 +4514,7 @@
   const QuicTime final_timeout = rto_time + initial_idle_timeout;
   clock_.AdvanceTime(rto_time - clock_.Now());
   ASSERT_EQ(rto_time, clock_.Now());
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _));
   connection_.GetRetransmissionAlarm()->Fire();
 
   // Advance to the original timeout and fire the alarm. The connection should
@@ -4618,7 +4651,7 @@
   // Retransmit the packet via tail loss probe.
   clock_.AdvanceTime(connection_.GetRetransmissionAlarm()->deadline() -
                      clock_.Now());
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _));
   connection_.GetRetransmissionAlarm()->Fire();
 
   // This time, we should time out and send a connection close due to the TLP.
@@ -4835,6 +4868,7 @@
   // Test that if we send a packet without delay, it is not queued.
   QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
   std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting);
+  QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet),
                          HAS_RETRANSMITTABLE_DATA, false, false);
@@ -4847,6 +4881,7 @@
   QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
   EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
   std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting);
+  QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
   writer_->SetShouldWriteFail();
   connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet),
                          HAS_RETRANSMITTABLE_DATA, false, false);
@@ -4855,8 +4890,10 @@
 TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) {
   QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
   std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting);
+  QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
   BlockOnNextWrite();
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _))
+      .Times(0);
   connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet),
                          HAS_RETRANSMITTABLE_DATA, false, false);
   EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -5108,7 +5145,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5224,7 +5261,7 @@
   // default delayed ack time.
   ack_time = clock_.ApproximateNow() +
              QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 4; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5300,7 +5337,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5353,7 +5390,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5416,7 +5453,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5485,7 +5522,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5574,7 +5611,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5647,7 +5684,7 @@
   frame1_.stream_id = 3;
 
   // Process all the initial packets in order so there aren't missing packets.
-  QuicPacketNumber kFirstDecimatedPacket = 101;
+  uint64_t kFirstDecimatedPacket = 101;
   for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
     EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
     ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL);
@@ -5844,7 +5881,7 @@
   EXPECT_EQ(1u, writer_->stream_frames().size());
   EXPECT_EQ(1u, writer_->padding_frames().size());
   ASSERT_FALSE(writer_->ack_frames().empty());
-  EXPECT_EQ(2u, LargestAcked(writer_->ack_frames().front()));
+  EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 }
 
@@ -5873,7 +5910,7 @@
   EXPECT_EQ(1u, writer_->stream_frames().size());
   EXPECT_EQ(1u, writer_->padding_frames().size());
   ASSERT_FALSE(writer_->ack_frames().empty());
-  EXPECT_EQ(2u, LargestAcked(writer_->ack_frames().front()));
+  EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 }
 
@@ -5888,7 +5925,7 @@
   // Ack the second packet, which will retransmit the first packet.
   QuicAckFrame ack = ConstructAckFrame(2, 1);
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(1, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize));
   EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
       .WillOnce(SetArgPointee<5>(lost_packets));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -5927,7 +5964,7 @@
     EXPECT_FALSE(writer_->stop_waiting_frames().empty());
   }
   EXPECT_FALSE(writer_->ack_frames().empty());
-  EXPECT_EQ(3u, LargestAcked(writer_->ack_frames().front()));
+  EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(writer_->ack_frames().front()));
   EXPECT_EQ(1u, writer_->stream_frames().size());
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 }
@@ -5950,7 +5987,8 @@
   EXPECT_FALSE(connection_.connected());
   EXPECT_FALSE(connection_.CanWriteStreamData());
   std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting);
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(0);
   connection_.SendPacket(ENCRYPTION_NONE, 1, std::move(packet),
                          HAS_RETRANSMITTABLE_DATA, false, false);
 }
@@ -5969,7 +6007,8 @@
   EXPECT_FALSE(connection_.connected());
   EXPECT_FALSE(connection_.CanWriteStreamData());
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(0);
 
   EXPECT_QUIC_BUG(connection_.SendConnectivityProbingPacket(
                       writer_.get(), connection_.peer_address()),
@@ -5987,7 +6026,8 @@
   // affects the probing_writer which is not the default.
   EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(1);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(1);
   connection_.SendConnectivityProbingPacket(&probing_writer,
                                             connection_.peer_address());
 }
@@ -6003,7 +6043,8 @@
   // writer to send connectivity probes.
   EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(1);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(1);
   connection_.SendConnectivityProbingPacket(writer_.get(),
                                             connection_.peer_address());
 }
@@ -6017,7 +6058,8 @@
   // sent.
   EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(0);
   connection_.SendConnectivityProbingPacket(&probing_writer,
                                             connection_.peer_address());
 }
@@ -6031,7 +6073,8 @@
   // sent.
   EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+      .Times(0);
   connection_.SendConnectivityProbingPacket(writer_.get(),
                                             connection_.peer_address());
 }
@@ -6144,14 +6187,14 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.version_flag = true;
-  header.packet_number = 12;
+  header.packet_number = QuicPacketNumber(12);
 
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
-  size_t encrypted_length = framer_.EncryptPayload(ENCRYPTION_NONE, 12, *packet,
-                                                   buffer, kMaxPacketSize);
+  size_t encrypted_length = framer_.EncryptPayload(
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
 
   framer_.set_version(version());
   // Writer's framer's perspective is client, so that it needs to have the right
@@ -6190,14 +6233,14 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.version_flag = true;
-  header.packet_number = 12;
+  header.packet_number = QuicPacketNumber(12);
 
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
-  size_t encrypted_length = framer_.EncryptPayload(ENCRYPTION_NONE, 12, *packet,
-                                                   buffer, kMaxPacketSize);
+  size_t encrypted_length = framer_.EncryptPayload(
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
 
   framer_.set_version(version());
   BlockOnNextWrite();
@@ -6243,14 +6286,14 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.version_flag = true;
-  header.packet_number = 12;
+  header.packet_number = QuicPacketNumber(12);
 
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
-  size_t encryped_length = framer_.EncryptPayload(ENCRYPTION_NONE, 12, *packet,
-                                                  buffer, kMaxPacketSize);
+  size_t encryped_length = framer_.EncryptPayload(
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
 
   framer_.set_version(version());
   set_perspective(Perspective::IS_SERVER);
@@ -6297,14 +6340,14 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
-  header.packet_number = 12;
+  header.packet_number = QuicPacketNumber(12);
   header.version_flag = false;
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
   std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = peer_framer_.EncryptPayload(
-      ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize);
+      ENCRYPTION_NONE, QuicPacketNumber(12), *packet, buffer, kMaxPacketSize);
   ASSERT_NE(0u, encrypted_length);
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -6355,11 +6398,13 @@
   connection_.GetRetransmissionAlarm()->Fire();
 
   // Retransmit due to explicit nacks.
-  QuicAckFrame nack_three = InitAckFrame({{2, 3}, {4, 5}});
+  QuicAckFrame nack_three =
+      InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)},
+                    {QuicPacketNumber(4), QuicPacketNumber(5)}});
 
   LostPacketVector lost_packets;
-  lost_packets.push_back(LostPacket(1, kMaxPacketSize));
-  lost_packets.push_back(LostPacket(3, kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(1), kMaxPacketSize));
+  lost_packets.push_back(LostPacket(QuicPacketNumber(3), kMaxPacketSize));
   EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
       .WillOnce(SetArgPointee<5>(lost_packets));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
@@ -6395,7 +6440,7 @@
   if (peer_framer_.transport_version() > QUIC_VERSION_43) {
     header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
   }
-  header.packet_number = 1;
+  header.packet_number = QuicPacketNumber(1);
   header.version_flag = false;
 
   QuicConnectionCloseFrame qccf;
@@ -6408,7 +6453,7 @@
   EXPECT_TRUE(nullptr != packet);
   char buffer[kMaxPacketSize];
   size_t encrypted_length = peer_framer_.EncryptPayload(
-      ENCRYPTION_NONE, 1, *packet, buffer, kMaxPacketSize);
+      ENCRYPTION_NONE, QuicPacketNumber(1), *packet, buffer, kMaxPacketSize);
 
   EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
                                            ConnectionCloseSource::FROM_PEER));
@@ -6490,7 +6535,7 @@
 
 TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
   QuicPacketHeader header;
-  header.packet_number = 1;
+  header.packet_number = QuicPacketNumber(1);
   if (GetParam().version.transport_version > QUIC_VERSION_43) {
     header.form = IETF_QUIC_LONG_HEADER_PACKET;
   }
@@ -6730,7 +6775,8 @@
       EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
     }
     EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-    QuicAckFrame frame = InitAckFrame({{1u + 2u * i, 2u + 2u * i}});
+    QuicAckFrame frame = InitAckFrame(
+        {{QuicPacketNumber(1u + 2u * i), QuicPacketNumber(2u + 2u * i)}});
     ProcessAckPacket(&frame);
     EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
     // Check the deadline of the path degrading alarm.
@@ -6745,7 +6791,7 @@
       // degrading alarm.
       clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
       EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-      frame = InitAckFrame({{2, 3}});
+      frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
       ProcessAckPacket(&frame);
       EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
     } else {
@@ -6800,7 +6846,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1, 2}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
   ProcessAckPacket(&frame);
   // No more retransmittable packets on the wire, so the path degrading alarm
   // should be cancelled, and the ping alarm should be set to the
@@ -6863,7 +6910,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1u, 2u}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
   // Check the deadline of the path degrading alarm.
@@ -6928,7 +6976,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1u, 2u}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
   // Check the deadline of the path degrading alarm.
@@ -6958,7 +7007,7 @@
   // degrading. And will set a timer to detect new path degrading.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  frame = InitAckFrame({{2, 3}});
+  frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
   ProcessAckPacket(&frame);
   EXPECT_FALSE(connection_.IsPathDegrading());
   EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
@@ -6981,7 +7030,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1u, 2u}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
   ProcessAckPacket(&frame);
   EXPECT_FALSE(connection_.IsPathDegrading());
   EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
@@ -7316,7 +7366,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1, 2}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
   // The ping alarm is set for the ping timeout, not the shorter
@@ -7333,7 +7384,7 @@
   // the wire.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  frame = InitAckFrame({{2, 3}});
+  frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
   EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
@@ -7343,7 +7394,7 @@
   // the ping alarm.
   QuicTime prev_deadline = connection_.GetPingAlarm()->deadline();
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
-  frame = InitAckFrame({{2, 3}});
+  frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
   EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline());
@@ -7400,7 +7451,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  QuicAckFrame frame = InitAckFrame({{1, 2}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
   EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
@@ -7418,7 +7470,7 @@
   // the wire.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
-  frame = InitAckFrame({{2, 3}});
+  frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
   ProcessAckPacket(&frame);
   EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
   EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
@@ -7458,13 +7510,14 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
   EXPECT_CALL(visitor_, OnForwardProgressConfirmed());
-  QuicAckFrame frame = InitAckFrame({{1, 2}});
+  QuicAckFrame frame =
+      InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
   ProcessAckPacket(&frame);
 
   // Ack packet 1 again. largest_acked remains at 1, so
   // OnForwardProgressConfirmed() should not be called.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
-  frame = InitAckFrame({{1, 2}});
+  frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
   ProcessAckPacket(&frame);
 
   // Ack packet 2. This increases the largest_acked to 2, so
@@ -7472,7 +7525,7 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
   EXPECT_CALL(visitor_, OnForwardProgressConfirmed());
-  frame = InitAckFrame({{2, 3}});
+  frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
   ProcessAckPacket(&frame);
 }
 
@@ -7661,7 +7714,7 @@
   std::unique_ptr<QuicPacket> packet(ConstructDataPacket(2, !kHasStopWaiting));
   char buffer[kMaxPacketSize];
   size_t encrypted_length = peer_framer_.EncryptPayload(
-      ENCRYPTION_NONE, 2, *packet, buffer, kMaxPacketSize);
+      ENCRYPTION_NONE, QuicPacketNumber(2), *packet, buffer, kMaxPacketSize);
   // Make sure no stream frame is processed.
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(0);
   connection_.ProcessUdpPacket(
diff --git a/net/third_party/quic/core/quic_constants.cc b/net/third_party/quic/core/quic_constants.cc
index 807b1e8..1a755ba 100644
--- a/net/third_party/quic/core/quic_constants.cc
+++ b/net/third_party/quic/core/quic_constants.cc
@@ -11,4 +11,15 @@
 const char* const kEPIDGoogleFrontEnd = "GFE";
 const char* const kEPIDGoogleFrontEnd0 = "GFE0";
 
+QuicPacketNumber MaxRandomInitialPacketNumber() {
+  static const QuicPacketNumber kMaxRandomInitialPacketNumber =
+      QuicPacketNumber(0x7fffffff);
+  return kMaxRandomInitialPacketNumber;
+}
+
+QuicPacketNumber FirstSendingPacketNumber() {
+  static const QuicPacketNumber kFirstSendingPacketNumber = QuicPacketNumber(1);
+  return kFirstSendingPacketNumber;
+}
+
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_constants.h b/net/third_party/quic/core/quic_constants.h
index e4b7d63..f17b6d2 100644
--- a/net/third_party/quic/core/quic_constants.h
+++ b/net/third_party/quic/core/quic_constants.h
@@ -193,7 +193,7 @@
 
 // For When using Random Initial Packet Numbers, they can start
 // anyplace in the range 1...((2^31)-1) or 0x7fffffff
-const QuicPacketNumber kMaxRandomInitialPacketNumber = 0x7fffffff;
+QUIC_EXPORT_PRIVATE QuicPacketNumber MaxRandomInitialPacketNumber();
 
 // Used to represent an invalid or no control frame id.
 const QuicControlFrameId kInvalidControlFrameId = 0;
@@ -227,10 +227,7 @@
 // Packet number of first sending packet of a connection. Please note, this
 // cannot be used as first received packet because peer can choose its starting
 // packet number.
-const QuicPacketNumber kFirstSendingPacketNumber = 1;
-
-// Used to represent an invalid packet number.
-const QuicPacketNumber kInvalidPacketNumber = 0;
+QUIC_EXPORT_PRIVATE QuicPacketNumber FirstSendingPacketNumber();
 
 // Used by clients to tell if a public reset is sent from a Google frontend.
 QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd;
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index 0ce7869..96b5aaf 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -513,27 +513,29 @@
   }
 
   // initial packet number of 0 is always invalid.
-  if (header.packet_number == kInvalidPacketNumber) {
+  if (!header.packet_number.IsInitialized()) {
     return kFateTimeWait;
   }
   if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
     QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 1, 2);
     // Accepting Initial Packet Numbers in 1...((2^31)-1) range... check
     // maximum accordingly.
-    if (header.packet_number > kMaxRandomInitialPacketNumber) {
+    if (header.packet_number > MaxRandomInitialPacketNumber()) {
       return kFateTimeWait;
     }
   } else {
     // Count those that would have been accepted if FLAGS..random_ipn
     // were true -- to detect/diagnose potential issues prior to
     // enabling the flag.
-    if ((header.packet_number > kMaxReasonableInitialPacketNumber) &&
-        (header.packet_number <= kMaxRandomInitialPacketNumber)) {
+    if ((header.packet_number >
+         QuicPacketNumber(kMaxReasonableInitialPacketNumber)) &&
+        (header.packet_number <= MaxRandomInitialPacketNumber())) {
       QUIC_CODE_COUNT_N(had_possibly_random_ipn, 1, 2);
     }
     // Check that the sequence number is within the range that the client is
     // expected to send before receiving a response from the server.
-    if (header.packet_number > kMaxReasonableInitialPacketNumber) {
+    if (header.packet_number >
+        QuicPacketNumber(kMaxReasonableInitialPacketNumber)) {
       return kFateTimeWait;
     }
   }
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index bd80f62..56bbd5f 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -110,7 +110,7 @@
   // send a handshake and then up to 50 or so data packets, and then it may
   // resend the handshake packet up to 10 times.  (Retransmitted packets are
   // sent with unique packet numbers.)
-  static const QuicPacketNumber kMaxReasonableInitialPacketNumber = 100;
+  static const uint64_t kMaxReasonableInitialPacketNumber = 100;
   static_assert(kMaxReasonableInitialPacketNumber >=
                     kInitialCongestionWindow + 10,
                 "kMaxReasonableInitialPacketNumber is unreasonably small "
diff --git a/net/third_party/quic/core/quic_dispatcher_test.cc b/net/third_party/quic/core/quic_dispatcher_test.cc
index 8f6aa01..c79f6c1 100644
--- a/net/third_party/quic/core/quic_dispatcher_test.cc
+++ b/net/third_party/quic/core/quic_dispatcher_test.cc
@@ -676,7 +676,7 @@
   SetQuicRestartFlag(quic_enable_accept_random_ipn, true);
   ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
                 PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
-                kMaxRandomInitialPacketNumber +
+                MaxRandomInitialPacketNumber().ToUint64() +
                     QuicDispatcher::kMaxReasonableInitialPacketNumber + 1);
 }
 
@@ -1956,7 +1956,7 @@
 
   // Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The
   // last one should be dropped.
-  for (QuicPacketNumber packet_number = 2;
+  for (uint64_t packet_number = 2;
        packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) {
     ProcessPacket(client_addr_, last_connection_id, true, "data packet");
   }
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index 6304730c..eb90e2d 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -142,6 +142,14 @@
   return (Delta(target, a) < Delta(target, b)) ? a : b;
 }
 
+uint64_t PacketNumberIntervalLength(
+    const QuicInterval<QuicPacketNumber>& interval) {
+  if (interval.Empty()) {
+    return 0u;
+  }
+  return interval.max() - interval.min();
+}
+
 QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) {
   switch (flags & PACKET_FLAGS_8BYTE_PACKET) {
     case PACKET_FLAGS_8BYTE_PACKET:
@@ -322,6 +330,12 @@
   return static_cast<uint8_t>(length - kConnectionIdLengthAdjustment);
 }
 
+bool IsValidPacketNumberLength(QuicPacketNumberLength packet_number_length) {
+  size_t length = packet_number_length;
+  return length == 1 || length == 2 || length == 4 || length == 6 ||
+         length == 8;
+}
+
 }  // namespace
 
 QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
@@ -329,7 +343,6 @@
                        Perspective perspective)
     : visitor_(nullptr),
       error_(QUIC_NO_ERROR),
-      largest_packet_number_(kInvalidPacketNumber),
       last_serialized_connection_id_(EmptyQuicConnectionId()),
       last_version_label_(0),
       last_header_form_(GOOGLE_QUIC_PACKET),
@@ -1255,7 +1268,7 @@
   } else {
     // Append an random packet number.
     QuicPacketNumber random_packet_number =
-        QuicRandom::GetInstance()->RandUint64() % 255 + 1;
+        QuicPacketNumber(QuicRandom::GetInstance()->RandUint64() % 255 + 1);
     if (!AppendPacketNumber(PACKET_1BYTE_PACKET_NUMBER, random_packet_number,
                             &writer)) {
       return nullptr;
@@ -1477,7 +1490,7 @@
       return RaiseError(QUIC_INVALID_PACKET_HEADER);
     }
 
-    if (full_packet_number == kInvalidPacketNumber) {
+    if (full_packet_number == 0u) {
       if (IsIetfStatelessResetPacket(*header)) {
         // This is a stateless reset packet.
         QuicIetfStatelessResetPacket packet(
@@ -1488,7 +1501,7 @@
       set_detailed_error("packet numbers cannot be 0.");
       return RaiseError(QUIC_INVALID_PACKET_HEADER);
     }
-    header->packet_number = full_packet_number;
+    header->packet_number = QuicPacketNumber(full_packet_number);
   }
 
   // A nonce should only present in SHLO from the server to the client when
@@ -1531,8 +1544,12 @@
 
   // Update the largest packet number after we have decrypted the packet
   // so we are confident is not attacker controlled.
-  largest_packet_number_ =
-      std::max(header->packet_number, largest_packet_number_);
+  if (largest_packet_number_.IsInitialized()) {
+    largest_packet_number_ =
+        std::max(header->packet_number, largest_packet_number_);
+  } else {
+    largest_packet_number_ = header->packet_number;
+  }
 
   if (!visitor_->OnPacketHeader(*header)) {
     // The visitor suppresses further processing of the packet.
@@ -1593,8 +1610,12 @@
 
   // Update the largest packet number after we have decrypted the packet
   // so we are confident is not attacker controlled.
-  largest_packet_number_ =
-      std::max(header->packet_number, largest_packet_number_);
+  if (largest_packet_number_.IsInitialized()) {
+    largest_packet_number_ =
+        std::max(header->packet_number, largest_packet_number_);
+  } else {
+    largest_packet_number_ = header->packet_number;
+  }
 
   if (!visitor_->OnPacketHeader(*header)) {
     // The visitor suppresses further processing of the packet.
@@ -1869,12 +1890,12 @@
   // epoch_delta is the delta between epochs the packet number was serialized
   // with, so the correct value is likely the same epoch as the last sequence
   // number or an adjacent epoch.
-  if (base_packet_number == kInvalidPacketNumber) {
+  if (!base_packet_number.IsInitialized()) {
     return packet_number;
   }
   const uint64_t epoch_delta = UINT64_C(1) << (8 * packet_number_length);
-  uint64_t next_packet_number = base_packet_number + 1;
-  uint64_t epoch = base_packet_number & ~(epoch_delta - 1);
+  uint64_t next_packet_number = base_packet_number.ToUint64() + 1;
+  uint64_t epoch = base_packet_number.ToUint64() & ~(epoch_delta - 1);
   uint64_t prev_epoch = epoch - epoch_delta;
   uint64_t next_epoch = epoch + epoch_delta;
 
@@ -1976,11 +1997,15 @@
 QuicPacketNumberLength QuicFramer::GetMinPacketNumberLength(
     QuicTransportVersion version,
     QuicPacketNumber packet_number) {
-  if (packet_number < 1 << (PACKET_1BYTE_PACKET_NUMBER * 8)) {
+  DCHECK(packet_number.IsInitialized());
+  if (packet_number < QuicPacketNumber(1 << (PACKET_1BYTE_PACKET_NUMBER * 8))) {
     return PACKET_1BYTE_PACKET_NUMBER;
-  } else if (packet_number < 1 << (PACKET_2BYTE_PACKET_NUMBER * 8)) {
+  } else if (packet_number <
+             QuicPacketNumber(1 << (PACKET_2BYTE_PACKET_NUMBER * 8))) {
     return PACKET_2BYTE_PACKET_NUMBER;
-  } else if (packet_number < UINT64_C(1) << (PACKET_4BYTE_PACKET_NUMBER * 8)) {
+  } else if (packet_number <
+             QuicPacketNumber(UINT64_C(1)
+                              << (PACKET_4BYTE_PACKET_NUMBER * 8))) {
     return PACKET_4BYTE_PACKET_NUMBER;
   } else {
     return PACKET_6BYTE_PACKET_NUMBER;
@@ -2018,7 +2043,7 @@
   new_ack_info.first_block_length = frame.packets.LastIntervalLength();
   auto itr = frame.packets.rbegin();
   QuicPacketNumber previous_start = itr->min();
-  new_ack_info.max_block_length = itr->Length();
+  new_ack_info.max_block_length = PacketNumberIntervalLength(*itr);
   ++itr;
 
   // Don't do any more work after getting information for 256 ACK blocks; any
@@ -2031,8 +2056,8 @@
     new_ack_info.num_ack_blocks +=
         (total_gap + std::numeric_limits<uint8_t>::max() - 1) /
         std::numeric_limits<uint8_t>::max();
-    new_ack_info.max_block_length =
-        std::max(new_ack_info.max_block_length, interval.Length());
+    new_ack_info.max_block_length = std::max(
+        new_ack_info.max_block_length, PacketNumberIntervalLength(interval));
   }
   return new_ack_info;
 }
@@ -2048,11 +2073,11 @@
     return RaiseError(QUIC_INVALID_PACKET_HEADER);
   }
 
-  if (full_packet_number == kInvalidPacketNumber) {
+  if (full_packet_number == 0u) {
     set_detailed_error("packet numbers cannot be 0.");
     return RaiseError(QUIC_INVALID_PACKET_HEADER);
   }
-  header->packet_number = full_packet_number;
+  header->packet_number = QuicPacketNumber(full_packet_number);
 
   if (!visitor_->OnUnauthenticatedHeader(*header)) {
     set_detailed_error(
@@ -2907,7 +2932,7 @@
   }
 
   if (!visitor_->OnAckFrameStart(
-          largest_acked,
+          QuicPacketNumber(largest_acked),
           ack_delay_time_us == kUFloat16MaxValue
               ? QuicTime::Delta::Infinite()
               : QuicTime::Delta::FromMicroseconds(ack_delay_time_us))) {
@@ -2958,8 +2983,9 @@
     return false;
   }
 
-  QuicPacketNumber first_received = largest_acked + 1 - first_block_length;
-  if (!visitor_->OnAckRange(first_received, largest_acked + 1)) {
+  uint64_t first_received = largest_acked + 1 - first_block_length;
+  if (!visitor_->OnAckRange(QuicPacketNumber(first_received),
+                            QuicPacketNumber(largest_acked + 1))) {
     // The visitor suppresses further processing of the packet. Although
     // this is not a parsing error, returns false as this is in middle
     // of processing an ack frame,
@@ -2995,8 +3021,9 @@
 
       first_received -= (gap + current_block_length);
       if (current_block_length > 0) {
-        if (!visitor_->OnAckRange(first_received,
-                                  first_received + current_block_length)) {
+        if (!visitor_->OnAckRange(
+                QuicPacketNumber(first_received),
+                QuicPacketNumber(first_received) + current_block_length)) {
           // The visitor suppresses further processing of the packet. Although
           // this is not a parsing error, returns false as this is in middle
           // of processing an ack frame,
@@ -3013,13 +3040,13 @@
     return false;
   }
 
-  if (!ProcessTimestampsInAckFrame(num_received_packets, largest_acked,
-                                   reader)) {
+  if (!ProcessTimestampsInAckFrame(num_received_packets,
+                                   QuicPacketNumber(largest_acked), reader)) {
     return false;
   }
 
   // Done processing the ACK frame.
-  return visitor_->OnAckFrameEnd(first_received);
+  return visitor_->OnAckFrameEnd(QuicPacketNumber(first_received));
 }
 
 bool QuicFramer::ProcessTimestampsInAckFrame(uint8_t num_received_packets,
@@ -3122,7 +3149,8 @@
     ack_frame->ect_1_count = 0;
     ack_frame->ecn_ce_count = 0;
   }
-  if (!visitor_->OnAckFrameStart(largest_acked, ack_frame->ack_delay_time)) {
+  if (!visitor_->OnAckFrameStart(QuicPacketNumber(largest_acked),
+                                 ack_frame->ack_delay_time)) {
     // The visitor suppresses further processing of the packet. Although this is
     // not a parsing error, returns false as this is in middle of processing an
     // ACK frame.
@@ -3147,8 +3175,8 @@
   }
   // Calculate the packets being acked in the first block.
   //  +1 because AddRange implementation requires [low,high)
-  QuicPacketNumber block_high = largest_acked + 1;
-  QuicPacketNumber block_low = largest_acked - ack_block_value;
+  uint64_t block_high = largest_acked + 1;
+  uint64_t block_low = largest_acked - ack_block_value;
 
   // ack_block_value is the number of packets preceding the
   // largest_acked packet which are in the block being acked. Thus,
@@ -3162,7 +3190,8 @@
     return false;
   }
 
-  if (!visitor_->OnAckRange(block_low, block_high)) {
+  if (!visitor_->OnAckRange(QuicPacketNumber(block_low),
+                            QuicPacketNumber(block_high))) {
     // The visitor suppresses further processing of the packet. Although
     // this is not a parsing error, returns false as this is in middle
     // of processing an ACK frame.
@@ -3215,7 +3244,8 @@
     // Calculate the low end of the new nth ack block. The +1 is
     // because the encoded value is the blocksize-1.
     block_low = block_high - 1 - ack_block_value;
-    if (!visitor_->OnAckRange(block_low, block_high)) {
+    if (!visitor_->OnAckRange(QuicPacketNumber(block_low),
+                              QuicPacketNumber(block_high))) {
       // The visitor suppresses further processing of the packet. Although
       // this is not a parsing error, returns false as this is in middle
       // of processing an ACK frame.
@@ -3227,7 +3257,7 @@
     ack_block_count--;
   }
 
-  return visitor_->OnAckFrameEnd(block_low);
+  return visitor_->OnAckFrameEnd(QuicPacketNumber(block_low));
 }
 
 bool QuicFramer::ProcessStopWaitingFrame(QuicDataReader* reader,
@@ -3239,7 +3269,7 @@
     set_detailed_error("Unable to read least unacked delta.");
     return false;
   }
-  if (header.packet_number < least_unacked_delta) {
+  if (header.packet_number.ToUint64() < least_unacked_delta) {
     set_detailed_error("Invalid unacked delta.");
     return false;
   }
@@ -3458,9 +3488,10 @@
                                   size_t total_len,
                                   size_t buffer_len,
                                   char* buffer) {
+  DCHECK(packet_number.IsInitialized());
   size_t output_length = 0;
   if (!encrypter_[level]->EncryptPacket(
-          version_.transport_version, packet_number,
+          version_.transport_version, packet_number.ToUint64(),
           QuicStringPiece(buffer, ad_len),  // Associated data
           QuicStringPiece(buffer + ad_len, total_len - ad_len),  // Plaintext
           buffer + ad_len,  // Destination buffer
@@ -3477,6 +3508,7 @@
                                   const QuicPacket& packet,
                                   char* buffer,
                                   size_t buffer_len) {
+  DCHECK(packet_number.IsInitialized());
   DCHECK(encrypter_[level] != nullptr);
 
   QuicStringPiece associated_data =
@@ -3488,7 +3520,7 @@
   // Encrypt the plaintext into the buffer.
   size_t output_length = 0;
   if (!encrypter_[level]->EncryptPacket(
-          version_.transport_version, packet_number, associated_data,
+          version_.transport_version, packet_number.ToUint64(), associated_data,
           packet.Plaintext(version_.transport_version), buffer + ad_len,
           &output_length, buffer_len - ad_len)) {
     RaiseError(QUIC_ENCRYPTION_FAILURE);
@@ -3530,8 +3562,9 @@
       header.nonce != nullptr, header.packet_number_length);
 
   bool success = decrypter_->DecryptPacket(
-      version_.transport_version, header.packet_number, associated_data,
-      encrypted, decrypted_buffer, decrypted_length, buffer_length);
+      version_.transport_version, header.packet_number.ToUint64(),
+      associated_data, encrypted, decrypted_buffer, decrypted_length,
+      buffer_length);
   if (success) {
     visitor_->OnDecryptedPacket(decrypter_level_);
   } else if (alternative_decrypter_ != nullptr) {
@@ -3553,8 +3586,9 @@
 
     if (try_alternative_decryption) {
       success = alternative_decrypter_->DecryptPacket(
-          version_.transport_version, header.packet_number, associated_data,
-          encrypted, decrypted_buffer, decrypted_length, buffer_length);
+          version_.transport_version, header.packet_number.ToUint64(),
+          associated_data, encrypted, decrypted_buffer, decrypted_length,
+          buffer_length);
     }
     if (success) {
       visitor_->OnDecryptedPacket(alternative_decrypter_level_);
@@ -3587,7 +3621,7 @@
   // Type byte, largest_acked, and delay_time are straight-forward.
   size_t ack_frame_size = kQuicFrameTypeSize;
   QuicPacketNumber largest_acked = LargestAcked(frame);
-  ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked);
+  ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked.ToUint64());
   uint64_t ack_delay_time_us;
   ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
   ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift;
@@ -3684,7 +3718,7 @@
   QuicPacketNumberLength largest_acked_length =
       GetMinPacketNumberLength(version_.transport_version, LargestAcked(ack));
   QuicPacketNumberLength ack_block_length = GetMinPacketNumberLength(
-      version_.transport_version, ack_info.max_block_length);
+      version_.transport_version, QuicPacketNumber(ack_info.max_block_length));
 
   ack_size =
       GetMinAckFrameSize(version_.transport_version, largest_acked_length);
@@ -3914,12 +3948,13 @@
 bool QuicFramer::AppendPacketNumber(QuicPacketNumberLength packet_number_length,
                                     QuicPacketNumber packet_number,
                                     QuicDataWriter* writer) {
-  size_t length = packet_number_length;
-  if (length != 1 && length != 2 && length != 4 && length != 6 && length != 8) {
-    QUIC_BUG << "Invalid packet_number_length: " << length;
+  DCHECK(packet_number.IsInitialized());
+  if (!IsValidPacketNumberLength(packet_number_length)) {
+    QUIC_BUG << "Invalid packet_number_length: " << packet_number_length;
     return false;
   }
-  return writer->WriteBytesToUInt64(packet_number_length, packet_number);
+  return writer->WriteBytesToUInt64(packet_number_length,
+                                    packet_number.ToUint64());
 }
 
 // static
@@ -3950,8 +3985,16 @@
                                 QuicPacketNumberLength length_length,
                                 uint64_t length,
                                 QuicDataWriter* writer) {
+  if (length == 0) {
+    if (!IsValidPacketNumberLength(length_length)) {
+      QUIC_BUG << "Invalid packet_number_length: " << length_length;
+      return false;
+    }
+    return writer->WriteUInt8(gap) &&
+           writer->WriteBytesToUInt64(length_length, length);
+  }
   return writer->WriteUInt8(gap) &&
-         AppendPacketNumber(length_length, length, writer);
+         AppendPacketNumber(length_length, QuicPacketNumber(length), writer);
 }
 
 bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame,
@@ -4152,8 +4195,9 @@
   QuicPacketNumber largest_acked = LargestAcked(frame);
   QuicPacketNumberLength largest_acked_length =
       GetMinPacketNumberLength(version_.transport_version, largest_acked);
-  QuicPacketNumberLength ack_block_length = GetMinPacketNumberLength(
-      version_.transport_version, new_ack_info.max_block_length);
+  QuicPacketNumberLength ack_block_length =
+      GetMinPacketNumberLength(version_.transport_version,
+                               QuicPacketNumber(new_ack_info.max_block_length));
   // Calculate available bytes for timestamps and ack blocks.
   int32_t available_timestamp_and_ack_block_bytes =
       writer->capacity() - writer->length() - ack_block_length -
@@ -4212,7 +4256,8 @@
   }
 
   // First ack block length.
-  if (!AppendPacketNumber(ack_block_length, new_ack_info.first_block_length,
+  if (!AppendPacketNumber(ack_block_length,
+                          QuicPacketNumber(new_ack_info.first_block_length),
                           writer)) {
     return false;
   }
@@ -4264,8 +4309,8 @@
           total_gap -
           (num_encoded_gaps - 1) * std::numeric_limits<uint8_t>::max();
       // Append the final ACK block with a non-empty size.
-      if (!AppendAckBlock(last_gap, ack_block_length, interval.Length(),
-                          writer)) {
+      if (!AppendAckBlock(last_gap, ack_block_length,
+                          PacketNumberIntervalLength(interval), writer)) {
         return false;
       }
       ++num_ack_blocks_written;
@@ -4357,11 +4402,11 @@
                                         const QuicStopWaitingFrame& frame,
                                         QuicDataWriter* writer) {
   DCHECK_GE(QUIC_VERSION_43, version_.transport_version);
-  DCHECK(frame.least_unacked != kInvalidPacketNumber &&
+  DCHECK(frame.least_unacked.IsInitialized() &&
          header.packet_number >= frame.least_unacked);
-  const QuicPacketNumber least_unacked_delta =
+  const uint64_t least_unacked_delta =
       header.packet_number - frame.least_unacked;
-  const QuicPacketNumber length_shift = header.packet_number_length * 8;
+  const uint64_t length_shift = header.packet_number_length * 8;
 
   if (least_unacked_delta >> length_shift > 0) {
     QUIC_BUG << "packet_number_length " << header.packet_number_length
@@ -4371,8 +4416,12 @@
              << " version:" << version_.transport_version;
     return false;
   }
-  if (!AppendPacketNumber(header.packet_number_length, least_unacked_delta,
-                          writer)) {
+  if (least_unacked_delta == 0) {
+    return writer->WriteBytesToUInt64(header.packet_number_length,
+                                      least_unacked_delta);
+  }
+  if (!AppendPacketNumber(header.packet_number_length,
+                          QuicPacketNumber(least_unacked_delta), writer)) {
     QUIC_BUG << " seq failed: " << header.packet_number_length;
     return false;
   }
@@ -4439,7 +4488,7 @@
   }
 
   QuicPacketNumber largest_acked = LargestAcked(frame);
-  if (!writer->WriteVarInt62(largest_acked)) {
+  if (!writer->WriteVarInt62(largest_acked.ToUint64())) {
     set_detailed_error("No room for largest-acked in ack frame");
     return false;
   }
@@ -4493,9 +4542,9 @@
   // Case 2 or 3
   auto itr = frame.packets.rbegin();
 
-  QuicPacketNumber ack_block_largest = largest_acked;
+  QuicPacketNumber ack_block_largest(largest_acked);
   QuicPacketNumber ack_block_smallest;
-  if ((itr->max() - 1) == largest_acked) {
+  if ((itr->max() - 1) == QuicPacketNumber(largest_acked)) {
     // If largest_acked + 1 is equal to the Max() of the first Interval
     // in the QuicAckFrame then the first Interval is the first ack block of the
     // frame; remaining Intervals are additional ack blocks.  The QuicAckFrame's
diff --git a/net/third_party/quic/core/quic_framer_test.cc b/net/third_party/quic/core/quic_framer_test.cc
index 43cde29..1bcb821b 100644
--- a/net/third_party/quic/core/quic_framer_test.cc
+++ b/net/third_party/quic/core/quic_framer_test.cc
@@ -51,10 +51,11 @@
   return TestConnectionId(UINT64_C(0xFEDCBA9876543211));
 }
 
-const QuicPacketNumber kPacketNumber = UINT64_C(0x12345678);
-const QuicPacketNumber kSmallLargestObserved = UINT16_C(0x1234);
-const QuicPacketNumber kSmallMissingPacket = UINT16_C(0x1233);
-const QuicPacketNumber kLeastUnacked = UINT64_C(0x012345670);
+const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678));
+const QuicPacketNumber kSmallLargestObserved =
+    QuicPacketNumber(UINT16_C(0x1234));
+const QuicPacketNumber kSmallMissingPacket = QuicPacketNumber(UINT16_C(0x1233));
+const QuicPacketNumber kLeastUnacked = QuicPacketNumber(UINT64_C(0x012345670));
 const QuicStreamId kStreamId = UINT64_C(0x01020304);
 // Note that the high 4 bits of the stream offset must be less than 0x40
 // in order to ensure that the value can be encoded using VarInt62 encoding.
@@ -65,7 +66,7 @@
 // This is the largest packet number that can be represented in IETF QUIC
 // varint62 format.
 const QuicPacketNumber kLargestIetfLargestObserved =
-    UINT64_C(0x3fffffffffffffff);
+    QuicPacketNumber(UINT64_C(0x3fffffffffffffff));
 // Encodings for the two bits in a VarInt62 that
 // describe the length of the VarInt61. For binary packet
 // formats in this file, the convention is to code the
@@ -90,7 +91,7 @@
                      size_t* output_length,
                      size_t max_output_length) override {
     version_ = version;
-    packet_number_ = packet_number;
+    packet_number_ = QuicPacketNumber(packet_number);
     associated_data_ = QuicString(associated_data);
     plaintext_ = QuicString(plaintext);
     memcpy(output, plaintext.data(), plaintext.length());
@@ -136,7 +137,7 @@
                      size_t* output_length,
                      size_t max_output_length) override {
     version_ = version;
-    packet_number_ = packet_number;
+    packet_number_ = QuicPacketNumber(packet_number);
     associated_data_ = QuicString(associated_data);
     ciphertext_ = QuicString(ciphertext);
     memcpy(output, ciphertext.data(), ciphertext.length());
@@ -631,12 +632,17 @@
 
 TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochStart) {
   // A few quick manual sanity checks.
-  CheckCalculatePacketNumber(UINT64_C(1), UINT64_C(0));
-  CheckCalculatePacketNumber(kEpoch + 1, kMask);
-  CheckCalculatePacketNumber(kEpoch, kMask);
+  CheckCalculatePacketNumber(UINT64_C(1), QuicPacketNumber());
+  CheckCalculatePacketNumber(kEpoch + 1, QuicPacketNumber(kMask));
+  CheckCalculatePacketNumber(kEpoch, QuicPacketNumber(kMask));
+  for (uint64_t j = 0; j < 10; j++) {
+    CheckCalculatePacketNumber(j, QuicPacketNumber());
+    CheckCalculatePacketNumber(kEpoch - 1 - j, QuicPacketNumber());
+  }
 
   // Cases where the last number was close to the start of the range.
-  for (QuicPacketNumber last = 0; last < 10; last++) {
+  for (QuicPacketNumber last = QuicPacketNumber(1); last < QuicPacketNumber(10);
+       last++) {
     // Small numbers should not wrap (even if they're out of order).
     for (uint64_t j = 0; j < 10; j++) {
       CheckCalculatePacketNumber(j, last);
@@ -652,7 +658,7 @@
 TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochEnd) {
   // Cases where the last number was close to the end of the range
   for (uint64_t i = 0; i < 10; i++) {
-    QuicPacketNumber last = kEpoch - i;
+    QuicPacketNumber last = QuicPacketNumber(kEpoch - i);
 
     // Small numbers should wrap.
     for (uint64_t j = 0; j < 10; j++) {
@@ -673,7 +679,7 @@
   const uint64_t cur_epoch = 2 * kEpoch;
   // Cases where the last number was close to the start of the range
   for (uint64_t i = 0; i < 10; i++) {
-    QuicPacketNumber last = cur_epoch + i;
+    QuicPacketNumber last = QuicPacketNumber(cur_epoch + i);
     // Small number should not wrap (even if they're out of order).
     for (uint64_t j = 0; j < 10; j++) {
       CheckCalculatePacketNumber(cur_epoch + j, last);
@@ -692,7 +698,7 @@
   const uint64_t next_epoch = 3 * kEpoch;
   // Cases where the last number was close to the end of the range
   for (uint64_t i = 0; i < 10; i++) {
-    QuicPacketNumber last = next_epoch - 1 - i;
+    QuicPacketNumber last = QuicPacketNumber(next_epoch - 1 - i);
 
     // Small numbers should wrap.
     for (uint64_t j = 0; j < 10; j++) {
@@ -715,7 +721,7 @@
   for (uint64_t i = 0; i < 10; i++) {
     // Subtract 1, because the expected next packet number is 1 more than the
     // last packet number.
-    QuicPacketNumber last = max_number - i - 1;
+    QuicPacketNumber last = QuicPacketNumber(max_number - i - 1);
 
     // Small numbers should not wrap, because they have nowhere to go.
     for (uint64_t j = 0; j < 10; j++) {
@@ -2639,7 +2645,7 @@
 
   EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
   ASSERT_TRUE(visitor_.header_.get());
-  EXPECT_EQ(0u, visitor_.header_->packet_number);
+  EXPECT_FALSE(visitor_.header_->packet_number.IsInitialized());
 }
 
 TEST_P(QuicFramerTest, AckFrameOneAckBlock) {
@@ -3175,7 +3181,8 @@
   const QuicAckFrame& frame = *visitor_.ack_frames_[0];
   EXPECT_EQ(1u, frame.packets.NumIntervals());
   EXPECT_EQ(kLargestIetfLargestObserved, LargestAcked(frame));
-  EXPECT_EQ(kLargestIetfLargestObserved, frame.packets.NumPacketsSlow());
+  EXPECT_EQ(kLargestIetfLargestObserved.ToUint64(),
+            frame.packets.NumPacketsSlow());
 }
 
 // This test looks for a malformed ack where
@@ -6244,9 +6251,9 @@
 
   // Use kSmallLargestObserved to make this test finished in a short time.
   QuicAckFrame ack_frame =
-      InitAckFrame({{1, 5},
-                    {10, 500},
-                    {900, kSmallMissingPacket},
+      InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)},
+                    {QuicPacketNumber(10), QuicPacketNumber(500)},
+                    {QuicPacketNumber(900), kSmallMissingPacket},
                     {kSmallMissingPacket + 1, kSmallLargestObserved + 1}});
   ack_frame.ack_delay_time = QuicTime::Delta::Zero();
 
@@ -6437,9 +6444,9 @@
   ack_frame.ack_delay_time = QuicTime::Delta::Zero();
   // 300 ack blocks.
   for (size_t i = 2; i < 2 * 300; i += 2) {
-    ack_frame.packets.Add(i);
+    ack_frame.packets.Add(QuicPacketNumber(i));
   }
-  ack_frame.packets.AddRange(600, kSmallLargestObserved + 1);
+  ack_frame.packets.AddRange(QuicPacketNumber(600), kSmallLargestObserved + 1);
 
   QuicFrames frames = {QuicFrame(&ack_frame)};
 
@@ -9110,10 +9117,10 @@
       QuicEncryptedPacket(buffer, encrypted_length, false)));
   ASSERT_EQ(1u, visitor_.ack_frames_.size());
   QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
-  EXPECT_EQ(600u, LargestAcked(processed_ack_frame));
+  EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame));
   ASSERT_EQ(256u, processed_ack_frame.packets.NumPacketsSlow());
-  EXPECT_EQ(90u, processed_ack_frame.packets.Min());
-  EXPECT_EQ(600u, processed_ack_frame.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(90u), processed_ack_frame.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max());
 }
 
 TEST_P(QuicFramerTest, AckTruncationSmallPacket) {
@@ -9150,10 +9157,10 @@
       QuicEncryptedPacket(buffer, encrypted_length, false)));
   ASSERT_EQ(1u, visitor_.ack_frames_.size());
   QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
-  EXPECT_EQ(600u, LargestAcked(processed_ack_frame));
+  EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame));
   ASSERT_EQ(240u, processed_ack_frame.packets.NumPacketsSlow());
-  EXPECT_EQ(122u, processed_ack_frame.packets.Min());
-  EXPECT_EQ(600u, processed_ack_frame.packets.Max());
+  EXPECT_EQ(QuicPacketNumber(122u), processed_ack_frame.packets.Min());
+  EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max());
 }
 
 TEST_P(QuicFramerTest, CleanTruncation) {
diff --git a/net/third_party/quic/core/quic_ietf_framer_test.cc b/net/third_party/quic/core/quic_ietf_framer_test.cc
index b92ff40..35ca93d8 100644
--- a/net/third_party/quic/core/quic_ietf_framer_test.cc
+++ b/net/third_party/quic/core/quic_ietf_framer_test.cc
@@ -728,35 +728,187 @@
 // Testing for the IETF ACK framer.
 // clang-format off
 struct ack_frame ack_frame_variants[] = {
-  { 90000, false, 0, 0, 0, {{1000, 2001}}, IETF_ACK },
-  { 0, false, 0, 0, 0, {{1000, 2001}}, IETF_ACK },
-  { 1, false, 0, 0, 0, {{1, 2}, {5, 6}}, IETF_ACK },
-  { 63, false, 0, 0, 0, {{1, 2}, {5, 6}}, IETF_ACK },
-  { 64, false, 0, 0, 0, {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}},
-    IETF_ACK},
-  { 10000, false, 0, 0, 0, {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}},
-    IETF_ACK},
-  { 100000000, false, 0, 0, 0,
-    {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}},
-    IETF_ACK},
-  { 0, false, 0, 0, 0, {{1, 65}}, IETF_ACK },
-  { 9223372036854775807, false, 0, 0, 0, {{1, 11}, {74, 138}}, IETF_ACK },
-  // This ack is for packets 60 & 125. There are 64 packets in the gap.
-  // The encoded value is gap_size - 1, or 63. Crosses a VarInt62 encoding
-  // boundary...
-  { 1, false, 0, 0, 0, {{60, 61}, {125, 126}}, IETF_ACK },
-  { 2, false, 0, 0, 0, {{ 1, 65}, {129, 130}}, IETF_ACK },
-  { 3, false, 0, 0, 0, {{ 1, 65}, {129, 195}}, IETF_ACK },
-  { 4, false, 0, 0, 0, {{ 1, 65}, {129, 194}}, IETF_ACK },
-  { 5, false, 0, 0, 0, {{ 1, 65}, {129, 193}}, IETF_ACK },
-  { 6, false, 0, 0, 0, {{ 1, 65}, {129, 192}}, IETF_ACK },
-  // declare some ack_ecn frames to try.
-  { 6, false, 100, 200, 300, {{ 1, 65}, {129, 192}}, IETF_ACK },
-  { 6, true, 100, 200, 300, {{ 1, 65}, {129, 192}}, IETF_ACK_ECN },
-  { 6, true, 100, 0, 0, {{ 1, 65}, {129, 192}}, IETF_ACK_ECN },
-  { 6, true, 0, 200, 0, {{ 1, 65}, {129, 192}}, IETF_ACK_ECN },
-  { 6, true, 0, 0, 300, {{ 1, 65}, {129, 192}}, IETF_ACK_ECN },
-  { 6, true, 0, 0, 0, {{ 1, 65}, {129, 192}}, IETF_ACK },
+    {90000,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1000), QuicPacketNumber(2001)}},
+     IETF_ACK},
+    {0,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1000), QuicPacketNumber(2001)}},
+     IETF_ACK},
+    {1,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(2)},
+      {QuicPacketNumber(5), QuicPacketNumber(6)}},
+     IETF_ACK},
+    {63,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(2)},
+      {QuicPacketNumber(5), QuicPacketNumber(6)}},
+     IETF_ACK},
+    {64,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(2)},
+      {QuicPacketNumber(3), QuicPacketNumber(4)},
+      {QuicPacketNumber(5), QuicPacketNumber(6)},
+      {QuicPacketNumber(7), QuicPacketNumber(8)},
+      {QuicPacketNumber(9), QuicPacketNumber(10)},
+      {QuicPacketNumber(11), QuicPacketNumber(12)}},
+     IETF_ACK},
+    {10000,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(2)},
+      {QuicPacketNumber(3), QuicPacketNumber(4)},
+      {QuicPacketNumber(5), QuicPacketNumber(6)},
+      {QuicPacketNumber(7), QuicPacketNumber(8)},
+      {QuicPacketNumber(9), QuicPacketNumber(10)},
+      {QuicPacketNumber(11), QuicPacketNumber(12)}},
+     IETF_ACK},
+    {100000000,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(2)},
+      {QuicPacketNumber(3), QuicPacketNumber(4)},
+      {QuicPacketNumber(5), QuicPacketNumber(6)},
+      {QuicPacketNumber(7), QuicPacketNumber(8)},
+      {QuicPacketNumber(9), QuicPacketNumber(10)},
+      {QuicPacketNumber(11), QuicPacketNumber(12)}},
+     IETF_ACK},
+    {0,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)}},
+     IETF_ACK},
+    {9223372036854775807,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(11)},
+      {QuicPacketNumber(74), QuicPacketNumber(138)}},
+     IETF_ACK},
+    // This ack is for packets 60 & 125. There are 64 packets in the gap.
+    // The encoded value is gap_size - 1, or 63. Crosses a VarInt62 encoding
+    // boundary...
+    {1,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(60), QuicPacketNumber(61)},
+      {QuicPacketNumber(125), QuicPacketNumber(126)}},
+     IETF_ACK},
+    {2,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(130)}},
+     IETF_ACK},
+    {3,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(195)}},
+     IETF_ACK},
+    {4,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(194)}},
+     IETF_ACK},
+    {5,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(193)}},
+     IETF_ACK},
+    {6,
+     false,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK},
+    // declare some ack_ecn frames to try.
+    {6,
+     false,
+     100,
+     200,
+     300,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK},
+    {6,
+     true,
+     100,
+     200,
+     300,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK_ECN},
+    {6,
+     true,
+     100,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK_ECN},
+    {6,
+     true,
+     0,
+     200,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK_ECN},
+    {6,
+     true,
+     0,
+     0,
+     300,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK_ECN},
+    {6,
+     true,
+     0,
+     0,
+     0,
+     {{QuicPacketNumber(1), QuicPacketNumber(65)},
+      {QuicPacketNumber(129), QuicPacketNumber(192)}},
+     IETF_ACK},
 };
 // clang-format on
 
@@ -782,7 +934,7 @@
                         NETWORK_BYTE_ORDER);
 
   QuicAckFrame transmit_frame;
-  transmit_frame.largest_acked = 1;
+  transmit_frame.largest_acked = QuicPacketNumber(1);
   transmit_frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(0);
 
   size_t expected_size =
diff --git a/net/third_party/quic/core/quic_packet_creator.cc b/net/third_party/quic/core/quic_packet_creator.cc
index 9fa7532..d629bc1 100644
--- a/net/third_party/quic/core/quic_packet_creator.cc
+++ b/net/third_party/quic/core/quic_packet_creator.cc
@@ -52,7 +52,7 @@
       connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
       packet_size_(0),
       connection_id_(connection_id),
-      packet_(kInvalidPacketNumber,
+      packet_(QuicPacketNumber(),
               PACKET_1BYTE_PACKET_NUMBER,
               nullptr,
               0,
@@ -132,7 +132,7 @@
       packet_.packet_number + 1 - least_packet_awaited_by_peer;
   const uint64_t delta = std::max(current_delta, max_packets_in_flight);
   packet_.packet_number_length = QuicFramer::GetMinPacketNumberLength(
-      framer_->transport_version(), delta * 4);
+      framer_->transport_version(), QuicPacketNumber(delta * 4));
 }
 
 bool QuicPacketCreator::ConsumeData(QuicStreamId id,
@@ -342,14 +342,14 @@
   packet_.has_stop_waiting = false;
   packet_.has_crypto_handshake = NOT_HANDSHAKE;
   packet_.num_padding_bytes = 0;
-  packet_.original_packet_number = kInvalidPacketNumber;
+  packet_.original_packet_number.Clear();
   if (!can_set_transmission_type_ || ShouldSetTransmissionTypeForNextFrame()) {
     packet_.transmission_type = NOT_RETRANSMISSION;
   }
   packet_.encrypted_buffer = nullptr;
   packet_.encrypted_length = 0;
   DCHECK(packet_.retransmittable_frames.empty());
-  packet_.largest_acked = kInvalidPacketNumber;
+  packet_.largest_acked.Clear();
   needs_full_padding_ = false;
 }
 
@@ -663,7 +663,7 @@
 
 // TODO(b/74062209): Make this a public method of framer?
 SerializedPacket QuicPacketCreator::NoPacket() {
-  return SerializedPacket(kInvalidPacketNumber, PACKET_1BYTE_PACKET_NUMBER,
+  return SerializedPacket(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER,
                           nullptr, 0, false, false);
 }
 
@@ -707,8 +707,8 @@
   } else {
     header->nonce = nullptr;
   }
-  if (packet_.packet_number == kInvalidPacketNumber) {
-    packet_.packet_number = kFirstSendingPacketNumber;
+  if (!packet_.packet_number.IsInitialized()) {
+    packet_.packet_number = FirstSendingPacketNumber();
   } else {
     ++packet_.packet_number;
   }
diff --git a/net/third_party/quic/core/quic_packet_creator_test.cc b/net/third_party/quic/core/quic_packet_creator_test.cc
index 4694ebf..fb73ce10 100644
--- a/net/third_party/quic/core/quic_packet_creator_test.cc
+++ b/net/third_party/quic/core/quic_packet_creator_test.cc
@@ -227,9 +227,10 @@
       int num_padding_bytes,
       EncryptionLevel encryption_level,
       QuicPacketNumberLength packet_number_length) {
-    return QuicPendingRetransmission(
-        1u, NOT_RETRANSMISSION, retransmittable_frames, has_crypto_handshake,
-        num_padding_bytes, encryption_level, packet_number_length);
+    return QuicPendingRetransmission(QuicPacketNumber(1u), NOT_RETRANSMISSION,
+                                     retransmittable_frames,
+                                     has_crypto_handshake, num_padding_bytes,
+                                     encryption_level, packet_number_length);
   }
 
   bool IsDefaultTestConfiguration() {
@@ -268,8 +269,7 @@
   for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; ++i) {
     EncryptionLevel level = static_cast<EncryptionLevel>(i);
     creator_.set_encryption_level(level);
-    frames_.push_back(
-        QuicFrame(new QuicAckFrame(InitAckFrame(QuicPacketNumber(1)))));
+    frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1))));
     frames_.push_back(QuicFrame(QuicStreamFrame(
         QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), false,
         0u, QuicStringPiece())));
@@ -513,8 +513,8 @@
   frames.push_back(QuicFrame(&frame));
   SerializedPacket serialized = SerializeAllFrames(frames);
   EXPECT_EQ(ENCRYPTION_NONE, serialized.encryption_level);
-  ASSERT_EQ(1u, serialized.packet_number);
-  ASSERT_EQ(1u, creator_.packet_number());
+  ASSERT_EQ(QuicPacketNumber(1u), serialized.packet_number);
+  ASSERT_EQ(QuicPacketNumber(1u), creator_.packet_number());
 
   InSequence s;
   EXPECT_CALL(framer_visitor_, OnPacket());
@@ -1016,28 +1016,33 @@
   }
 
   QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64);
-  creator_.UpdatePacketNumberLength(2, 10000 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+                                    10000 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
   QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256);
-  creator_.UpdatePacketNumberLength(2, 10000 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+                                    10000 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
   QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256 * 256);
-  creator_.UpdatePacketNumberLength(2, 10000 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+                                    10000 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
   QuicPacketCreatorPeer::SetPacketNumber(&creator_,
                                          UINT64_C(64) * 256 * 256 * 256 * 256);
-  creator_.UpdatePacketNumberLength(2, 10000 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+                                    10000 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 }
 
 TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) {
+  QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1);
   if (GetParam().version.transport_version > QUIC_VERSION_43 &&
       GetParam().version.transport_version != QUIC_VERSION_99) {
     EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
@@ -1048,21 +1053,24 @@
               QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
   }
 
-  creator_.UpdatePacketNumberLength(1, 10000 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
+                                    10000 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
-  creator_.UpdatePacketNumberLength(1, 10000 * 256 / kDefaultMaxPacketSize);
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
+                                    10000 * 256 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
-  creator_.UpdatePacketNumberLength(1,
+  creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
                                     10000 * 256 * 256 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 
   creator_.UpdatePacketNumberLength(
-      1, UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize);
+      QuicPacketNumber(1),
+      UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize);
   EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER,
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 }
@@ -1181,7 +1189,7 @@
   ASSERT_EQ(1u, retransmittable.size());
   EXPECT_EQ(STREAM_FRAME, retransmittable[0].type);
   EXPECT_TRUE(serialized_packet_.has_ack);
-  EXPECT_EQ(10u, serialized_packet_.largest_acked);
+  EXPECT_EQ(QuicPacketNumber(10u), serialized_packet_.largest_acked);
   DeleteSerializedPacket();
 
   EXPECT_FALSE(creator_.HasPendingFrames());
@@ -1466,7 +1474,9 @@
 // failure. While this test is not applicable to versions other than version 99,
 // it should still work. Hence, it is not made version-specific.
 TEST_P(QuicPacketCreatorTest, IetfAckGapErrorRegression) {
-  QuicAckFrame ack_frame = InitAckFrame({{60, 61}, {125, 126}});
+  QuicAckFrame ack_frame =
+      InitAckFrame({{QuicPacketNumber(60), QuicPacketNumber(61)},
+                    {QuicPacketNumber(125), QuicPacketNumber(126)}});
   frames_.push_back(QuicFrame(&ack_frame));
   SerializeAllFrames(frames_);
 }
diff --git a/net/third_party/quic/core/quic_packet_generator_test.cc b/net/third_party/quic/core/quic_packet_generator_test.cc
index ab3b28728..f1d6201 100644
--- a/net/third_party/quic/core/quic_packet_generator_test.cc
+++ b/net/third_party/quic/core/quic_packet_generator_test.cc
@@ -155,7 +155,7 @@
                    &delegate_,
                    &producer_),
         creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)),
-        ack_frame_(InitAckFrame(QuicPacketNumber(1))) {
+        ack_frame_(InitAckFrame(1)) {
     EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr));
     creator_->SetEncrypter(
         ENCRYPTION_FORWARD_SECURE,
diff --git a/net/third_party/quic/core/quic_packet_number.cc b/net/third_party/quic/core/quic_packet_number.cc
new file mode 100644
index 0000000..ea6456b6
--- /dev/null
+++ b/net/third_party/quic/core/quic_packet_number.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/quic_packet_number.h"
+
+namespace quic {
+namespace {
+const uint64_t kUninitializedPacketNumber = 0;
+}  // namespace
+
+QuicPacketNumber::QuicPacketNumber()
+    : packet_number_(kUninitializedPacketNumber) {}
+
+QuicPacketNumber::QuicPacketNumber(uint64_t packet_number)
+    : packet_number_(packet_number) {
+  DCHECK_NE(kUninitializedPacketNumber, packet_number)
+      << "Use default constructor for uninitialized packet number";
+}
+
+void QuicPacketNumber::Clear() {
+  packet_number_ = kUninitializedPacketNumber;
+}
+
+uint64_t QuicPacketNumber::Hash() const {
+  DCHECK(IsInitialized());
+  return packet_number_;
+}
+
+uint64_t QuicPacketNumber::ToUint64() const {
+  DCHECK(IsInitialized());
+  return packet_number_;
+}
+
+bool QuicPacketNumber::IsInitialized() const {
+  return packet_number_ != kUninitializedPacketNumber;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator++() {
+  DCHECK(IsInitialized() && ToUint64() < std::numeric_limits<uint64_t>::max());
+  packet_number_++;
+  return *this;
+}
+
+QuicPacketNumber QuicPacketNumber::operator++(int) {
+  DCHECK(IsInitialized() && ToUint64() < std::numeric_limits<uint64_t>::max());
+  QuicPacketNumber previous(*this);
+  packet_number_++;
+  return previous;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator--() {
+  DCHECK(IsInitialized() && ToUint64() > 1);
+  packet_number_--;
+  return *this;
+}
+
+QuicPacketNumber QuicPacketNumber::operator--(int) {
+  DCHECK(IsInitialized() && ToUint64() > 1);
+  QuicPacketNumber previous(*this);
+  packet_number_--;
+  return previous;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator+=(uint64_t delta) {
+  DCHECK(IsInitialized() &&
+         std::numeric_limits<uint64_t>::max() - ToUint64() >= delta);
+  packet_number_ += delta;
+  return *this;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator-=(uint64_t delta) {
+  DCHECK(IsInitialized() && ToUint64() > delta);
+  packet_number_ -= delta;
+  return *this;
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicPacketNumber& p) {
+  if (p.IsInitialized()) {
+    os << p.packet_number_;
+  } else {
+    os << "uninitialized";
+  }
+  return os;
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_number.h b/net/third_party/quic/core/quic_packet_number.h
new file mode 100644
index 0000000..c78ea37
--- /dev/null
+++ b/net/third_party/quic/core/quic_packet_number.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_QUIC_CORE_QUIC_PACKET_NUMBER_H_
+#define NET_THIRD_PARTY_QUIC_CORE_QUIC_PACKET_NUMBER_H_
+
+#include <limits>
+#include <ostream>
+
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_logging.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+// QuicPacketNumber can either initialized or uninitialized. An initialized
+// packet number is simply an ordinal number. A sentinel value is used to
+// represent an uninitialized packet number.
+class QUIC_EXPORT_PRIVATE QuicPacketNumber {
+ public:
+  // Construct an uninitialized packet number.
+  QuicPacketNumber();
+  // Construct a packet number from uint64_t. |packet_number| cannot equal the
+  // sentinel value.
+  explicit QuicPacketNumber(uint64_t packet_number);
+
+  // Packet number becomes uninitialized after calling this function.
+  void Clear();
+
+  // REQUIRES: IsInitialized() == true.
+  uint64_t Hash() const;
+
+  // Converts packet number to uint64_t.
+  // REQUIRES: IsInitialized() == true.
+  uint64_t ToUint64() const;
+
+  // Returns true if packet number is considered initialized.
+  bool IsInitialized() const;
+
+  // REQUIRES: IsInitialized() == true && ToUint64() <
+  // numeric_limits<uint64_t>::max().
+  QuicPacketNumber& operator++();
+  QuicPacketNumber operator++(int);
+  // REQUIRES: IsInitialized() == true && ToUint64() > 1.
+  QuicPacketNumber& operator--();
+  QuicPacketNumber operator--(int);
+
+  // REQUIRES: IsInitialized() == true && numeric_limits<uint64_t>::max() -
+  // ToUint64() >= |delta|.
+  QuicPacketNumber& operator+=(uint64_t delta);
+  // REQUIRES: IsInitialized() == true && ToUint64() > |delta|.
+  QuicPacketNumber& operator-=(uint64_t delta);
+
+  QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+      std::ostream& os,
+      const QuicPacketNumber& p);
+
+ private:
+  // All following operators REQUIRE operands.Initialized() == true.
+  friend inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs);
+  friend inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+  friend inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs);
+  friend inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+  friend inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs);
+  friend inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+
+  // REQUIRES: numeric_limits<uint64_t>::max() - lhs.ToUint64() >= |delta|.
+  friend inline QuicPacketNumber operator+(QuicPacketNumber lhs,
+                                           uint64_t delta);
+  // REQUIRES: lhs.ToUint64() > |delta|.
+  friend inline QuicPacketNumber operator-(QuicPacketNumber lhs,
+                                           uint64_t delta);
+  // REQUIRES: lhs >= rhs.
+  friend inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs);
+
+  uint64_t packet_number_;
+};
+
+class QuicPacketNumberHash {
+ public:
+  uint64_t operator()(QuicPacketNumber packet_number) const noexcept {
+    return packet_number.Hash();
+  }
+};
+
+inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ == rhs.packet_number_;
+}
+
+inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ != rhs.packet_number_;
+}
+
+inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ < rhs.packet_number_;
+}
+
+inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ <= rhs.packet_number_;
+}
+
+inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ > rhs.packet_number_;
+}
+
+inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+  return lhs.packet_number_ >= rhs.packet_number_;
+}
+
+inline QuicPacketNumber operator+(QuicPacketNumber lhs, uint64_t delta) {
+  DCHECK(lhs.IsInitialized() &&
+         std::numeric_limits<uint64_t>::max() - lhs.ToUint64() >= delta);
+  return QuicPacketNumber(lhs.packet_number_ + delta);
+}
+
+inline QuicPacketNumber operator-(QuicPacketNumber lhs, uint64_t delta) {
+  DCHECK(lhs.IsInitialized() && lhs.ToUint64() > delta);
+  return QuicPacketNumber(lhs.packet_number_ - delta);
+}
+
+inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+  DCHECK(lhs.IsInitialized() && rhs.IsInitialized() && lhs >= rhs)
+      << lhs << " vs. " << rhs;
+  return lhs.packet_number_ - rhs.packet_number_;
+}
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_QUIC_PACKET_NUMBER_H_
diff --git a/net/third_party/quic/core/quic_packet_number_test.cc b/net/third_party/quic/core/quic_packet_number_test.cc
new file mode 100644
index 0000000..ded7ba6
--- /dev/null
+++ b/net/third_party/quic/core/quic_packet_number_test.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/quic_packet_number.h"
+
+#include "net/third_party/quic/platform/api/quic_test.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+TEST(QuicPacketNumberTest, BasicTest) {
+  QuicPacketNumber num;
+  EXPECT_FALSE(num.IsInitialized());
+
+  QuicPacketNumber num2(10);
+  EXPECT_TRUE(num2.IsInitialized());
+  EXPECT_EQ(10u, num2.ToUint64());
+  EXPECT_EQ(10u, num2.Hash());
+  num2.Clear();
+  EXPECT_FALSE(num2.IsInitialized());
+
+  QuicPacketNumber num3(std::numeric_limits<uint64_t>::max());
+  EXPECT_TRUE(num3.IsInitialized());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.ToUint64());
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.Hash());
+  num3.Clear();
+  EXPECT_FALSE(num3.IsInitialized());
+}
+
+TEST(QuicPacketNumberTest, Operators) {
+  QuicPacketNumber num(100);
+  EXPECT_EQ(QuicPacketNumber(100), num++);
+  EXPECT_EQ(QuicPacketNumber(101), num);
+  EXPECT_EQ(QuicPacketNumber(101), num--);
+  EXPECT_EQ(QuicPacketNumber(100), num);
+
+  EXPECT_EQ(QuicPacketNumber(101), ++num);
+  EXPECT_EQ(QuicPacketNumber(100), --num);
+}
+
+}  // namespace
+
+}  // namespace test
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/quic_packets.cc b/net/third_party/quic/core/quic_packets.cc
index 0047fd7..34e95e5 100644
--- a/net/third_party/quic/core/quic_packets.cc
+++ b/net/third_party/quic/core/quic_packets.cc
@@ -77,7 +77,6 @@
       version(
           ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED)),
       nonce(nullptr),
-      packet_number(kInvalidPacketNumber),
       form(GOOGLE_QUIC_PACKET),
       long_packet_type(INITIAL),
       possible_stateless_reset_token(0) {}
@@ -294,9 +293,7 @@
       encryption_level(ENCRYPTION_NONE),
       has_ack(has_ack),
       has_stop_waiting(has_stop_waiting),
-      transmission_type(NOT_RETRANSMISSION),
-      original_packet_number(kInvalidPacketNumber),
-      largest_acked(kInvalidPacketNumber) {}
+      transmission_type(NOT_RETRANSMISSION) {}
 
 SerializedPacket::SerializedPacket(const SerializedPacket& other) = default;
 
@@ -327,7 +324,7 @@
   }
   serialized_packet->encrypted_buffer = nullptr;
   serialized_packet->encrypted_length = 0;
-  serialized_packet->largest_acked = kInvalidPacketNumber;
+  serialized_packet->largest_acked.Clear();
 }
 
 char* CopyBuffer(const SerializedPacket& packet) {
diff --git a/net/third_party/quic/core/quic_received_packet_manager.cc b/net/third_party/quic/core/quic_received_packet_manager.cc
index 53f1043..aad7aa9 100644
--- a/net/third_party/quic/core/quic_received_packet_manager.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager.cc
@@ -25,8 +25,7 @@
 }  // namespace
 
 QuicReceivedPacketManager::QuicReceivedPacketManager(QuicConnectionStats* stats)
-    : peer_least_packet_awaiting_ack_(0),
-      ack_frame_updated_(false),
+    : ack_frame_updated_(false),
       max_ack_ranges_(0),
       time_largest_observed_(QuicTime::Zero()),
       save_timestamps_(false),
@@ -44,7 +43,8 @@
   }
   ack_frame_updated_ = true;
 
-  if (LargestAcked(ack_frame_) > packet_number) {
+  if (LargestAcked(ack_frame_).IsInitialized() &&
+      LargestAcked(ack_frame_) > packet_number) {
     // Record how out of order stats.
     ++stats_->packets_reordered;
     stats_->max_sequence_reordering =
@@ -55,7 +55,8 @@
     stats_->max_time_reordering_us =
         std::max(stats_->max_time_reordering_us, reordering_time_us);
   }
-  if (packet_number > LargestAcked(ack_frame_)) {
+  if (!LargestAcked(ack_frame_).IsInitialized() ||
+      packet_number > LargestAcked(ack_frame_)) {
     ack_frame_.largest_acked = packet_number;
     time_largest_observed_ = receipt_time;
   }
@@ -77,7 +78,8 @@
 }
 
 bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) {
-  return packet_number < LargestAcked(ack_frame_) &&
+  return LargestAcked(ack_frame_).IsInitialized() &&
+         packet_number < LargestAcked(ack_frame_) &&
          !ack_frame_.packets.Contains(packet_number);
 }
 
@@ -120,9 +122,14 @@
 
 void QuicReceivedPacketManager::DontWaitForPacketsBefore(
     QuicPacketNumber least_unacked) {
+  if (!least_unacked.IsInitialized()) {
+    return;
+  }
   // ValidateAck() should fail if peer_least_packet_awaiting_ack shrinks.
-  DCHECK_LE(peer_least_packet_awaiting_ack_, least_unacked);
-  if (least_unacked > peer_least_packet_awaiting_ack_) {
+  DCHECK(!peer_least_packet_awaiting_ack_.IsInitialized() ||
+         peer_least_packet_awaiting_ack_ <= least_unacked);
+  if (!peer_least_packet_awaiting_ack_.IsInitialized() ||
+      least_unacked > peer_least_packet_awaiting_ack_) {
     peer_least_packet_awaiting_ack_ = least_unacked;
     bool packets_updated = ack_frame_.packets.RemoveUpTo(least_unacked);
     if (packets_updated) {
@@ -132,14 +139,23 @@
     }
   }
   DCHECK(ack_frame_.packets.Empty() ||
+         !peer_least_packet_awaiting_ack_.IsInitialized() ||
          ack_frame_.packets.Min() >= peer_least_packet_awaiting_ack_);
 }
 
 bool QuicReceivedPacketManager::HasMissingPackets() const {
-  return ack_frame_.packets.NumIntervals() > 1 ||
-         (!ack_frame_.packets.Empty() &&
-          ack_frame_.packets.Min() >
-              std::max(QuicPacketNumber(1), peer_least_packet_awaiting_ack_));
+  if (ack_frame_.packets.Empty()) {
+    return false;
+  }
+  if (ack_frame_.packets.NumIntervals() > 1) {
+    return true;
+  }
+  // TODO(fayang): Fix this as this check assumes first sent packet by peer
+  // is 1.
+  return ack_frame_.packets.Min() >
+         (peer_least_packet_awaiting_ack_.IsInitialized()
+              ? peer_least_packet_awaiting_ack_
+              : QuicPacketNumber(1));
 }
 
 bool QuicReceivedPacketManager::HasNewMissingPackets() const {
diff --git a/net/third_party/quic/core/quic_received_packet_manager_test.cc b/net/third_party/quic/core/quic_received_packet_manager_test.cc
index cfa42ff..4440e52 100644
--- a/net/third_party/quic/core/quic_received_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager_test.cc
@@ -42,14 +42,13 @@
     received_manager_.set_save_timestamps(true);
   }
 
-  void RecordPacketReceipt(QuicPacketNumber packet_number) {
+  void RecordPacketReceipt(uint64_t packet_number) {
     RecordPacketReceipt(packet_number, QuicTime::Zero());
   }
 
-  void RecordPacketReceipt(QuicPacketNumber packet_number,
-                           QuicTime receipt_time) {
+  void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time) {
     QuicPacketHeader header;
-    header.packet_number = packet_number;
+    header.packet_number = QuicPacketNumber(packet_number);
     received_manager_.RecordPacketReceived(header, receipt_time);
   }
 
@@ -63,20 +62,20 @@
 
 TEST_P(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
   QuicPacketHeader header;
-  header.packet_number = 2u;
+  header.packet_number = QuicPacketNumber(2u);
   received_manager_.RecordPacketReceived(header, QuicTime::Zero());
-  header.packet_number = 7u;
+  header.packet_number = QuicPacketNumber(7u);
   received_manager_.RecordPacketReceived(header, QuicTime::Zero());
-  EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u));
-  EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
-  received_manager_.DontWaitForPacketsBefore(4);
-  EXPECT_FALSE(received_manager_.IsAwaitingPacket(3u));
-  EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
+  EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u)));
+  EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u)));
+  received_manager_.DontWaitForPacketsBefore(QuicPacketNumber(4));
+  EXPECT_FALSE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u)));
+  EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u)));
 }
 
 TEST_P(QuicReceivedPacketManagerTest, GetUpdatedAckFrame) {
   QuicPacketHeader header;
-  header.packet_number = 2u;
+  header.packet_number = QuicPacketNumber(2u);
   QuicTime two_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
   EXPECT_FALSE(received_manager_.ack_frame_updated());
   received_manager_.RecordPacketReceived(header, two_ms);
@@ -99,11 +98,11 @@
   // And received packet times won't have change.
   EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size());
 
-  header.packet_number = 999u;
+  header.packet_number = QuicPacketNumber(999u);
   received_manager_.RecordPacketReceived(header, two_ms);
-  header.packet_number = 4u;
+  header.packet_number = QuicPacketNumber(4u);
   received_manager_.RecordPacketReceived(header, two_ms);
-  header.packet_number = 1000u;
+  header.packet_number = QuicPacketNumber(1000u);
   received_manager_.RecordPacketReceived(header, two_ms);
   EXPECT_TRUE(received_manager_.ack_frame_updated());
   ack = received_manager_.GetUpdatedAckFrame(two_ms);
@@ -134,11 +133,16 @@
     EXPECT_TRUE(received_manager_.ack_frame_updated());
     received_manager_.GetUpdatedAckFrame(QuicTime::Zero());
     EXPECT_GE(10u, received_manager_.ack_frame().packets.NumIntervals());
-    EXPECT_EQ(1u + 2 * i, received_manager_.ack_frame().packets.Max());
+    EXPECT_EQ(QuicPacketNumber(1u + 2 * i),
+              received_manager_.ack_frame().packets.Max());
     for (int j = 0; j < std::min(10, i + 1); ++j) {
-      EXPECT_TRUE(
-          received_manager_.ack_frame().packets.Contains(1 + (i - j) * 2));
-      EXPECT_FALSE(received_manager_.ack_frame().packets.Contains((i - j) * 2));
+      ASSERT_GE(i, j);
+      EXPECT_TRUE(received_manager_.ack_frame().packets.Contains(
+          QuicPacketNumber(1 + (i - j) * 2)));
+      if (i > j) {
+        EXPECT_FALSE(received_manager_.ack_frame().packets.Contains(
+            QuicPacketNumber((i - j) * 2)));
+      }
     }
   }
 }
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.cc b/net/third_party/quic/core/quic_sent_packet_manager.cc
index e3258685..07420b47 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager.cc
+++ b/net/third_party/quic/core/quic_sent_packet_manager.cc
@@ -86,7 +86,6 @@
       loss_algorithm_(&general_loss_algorithm_),
       general_loss_algorithm_(loss_type),
       n_connection_simulation_(false),
-      first_rto_transmission_(0),
       consecutive_rto_count_(0),
       consecutive_tlp_count_(0),
       consecutive_crypto_retransmission_count_(0),
@@ -103,10 +102,8 @@
           QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs)),
       ietf_style_tlp_(false),
       ietf_style_2x_tlp_(false),
-      largest_newly_acked_(0),
       largest_mtu_acked_(0),
       handshake_confirmed_(false),
-      largest_packet_peer_knows_is_acked_(0),
       delayed_ack_time_(
           QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)),
       rtt_updated_(false),
@@ -452,7 +449,7 @@
   } else {
     // Clear the recorded first packet sent after loss when version or
     // encryption changes.
-    transmission_info->retransmission = kInvalidPacketNumber;
+    transmission_info->retransmission.Clear();
   }
 }
 
@@ -479,7 +476,7 @@
     return;
   }
   QuicPacketNumber retransmission = info.retransmission;
-  while (retransmission != kInvalidPacketNumber) {
+  while (retransmission.IsInitialized()) {
     const QuicTransmissionInfo& retransmit_info =
         unacked_packets_.GetTransmissionInfo(retransmission);
     retransmission = retransmit_info.retransmission;
@@ -529,7 +526,7 @@
     return packet_number;
   }
   QuicPacketNumber retransmission = transmission_info.retransmission;
-  while (retransmission != kInvalidPacketNumber) {
+  while (retransmission.IsInitialized()) {
     packet_number = retransmission;
     retransmission =
         unacked_packets_.GetTransmissionInfo(retransmission).retransmission;
@@ -607,12 +604,12 @@
     TransmissionType transmission_type,
     HasRetransmittableData has_retransmittable_data) {
   QuicPacketNumber packet_number = serialized_packet->packet_number;
-  DCHECK_LT(0u, packet_number);
+  DCHECK_LE(FirstSendingPacketNumber(), packet_number);
   DCHECK(!unacked_packets_.IsUnacked(packet_number));
   QUIC_BUG_IF(serialized_packet->encrypted_length == 0)
       << "Cannot send empty packets.";
 
-  if (original_packet_number != kInvalidPacketNumber) {
+  if (original_packet_number.IsInitialized()) {
     pending_retransmissions_.erase(original_packet_number);
   }
 
@@ -758,7 +755,7 @@
     }
     // Abandon non-retransmittable data that's in flight to ensure it doesn't
     // fill up the congestion window.
-    bool has_retransmissions = it->retransmission != kInvalidPacketNumber;
+    bool has_retransmissions = it->retransmission.IsInitialized();
     if (session_decides_what_to_write()) {
       has_retransmissions = it->state != OUTSTANDING;
     }
@@ -1051,21 +1048,23 @@
   DCHECK_LE(largest_acked, unacked_packets_.largest_sent_packet());
   rtt_updated_ =
       MaybeUpdateRTT(largest_acked, ack_delay_time, ack_receive_time);
-  DCHECK_GE(largest_acked, unacked_packets_.largest_acked());
+  DCHECK(!unacked_packets_.largest_acked().IsInitialized() ||
+         largest_acked >= unacked_packets_.largest_acked());
   last_ack_frame_.ack_delay_time = ack_delay_time;
   acked_packets_iter_ = last_ack_frame_.packets.rbegin();
 }
 
 void QuicSentPacketManager::OnAckRange(QuicPacketNumber start,
                                        QuicPacketNumber end) {
-  if (end > last_ack_frame_.largest_acked + 1) {
+  if (!last_ack_frame_.largest_acked.IsInitialized() ||
+      end > last_ack_frame_.largest_acked + 1) {
     // Largest acked increases.
     unacked_packets_.IncreaseLargestAcked(end - 1);
     last_ack_frame_.largest_acked = end - 1;
   }
   // Drop ack ranges which ack packets below least_unacked.
   QuicPacketNumber least_unacked = unacked_packets_.GetLeastUnacked();
-  if (end <= least_unacked) {
+  if (least_unacked.IsInitialized() && end <= least_unacked) {
     return;
   }
   start = std::max(start, least_unacked);
@@ -1079,6 +1078,9 @@
       // Check if end is above the current range. If so add newly acked packets
       // in descending order.
       packets_acked_.push_back(AckedPacket(acked, 0, QuicTime::Zero()));
+      if (acked == FirstSendingPacketNumber()) {
+        break;
+      }
     }
     if (acked_packets_iter_ == last_ack_frame_.packets.rend() ||
         start > acked_packets_iter_->min()) {
@@ -1125,9 +1127,13 @@
     QUIC_DVLOG(1) << ENDPOINT << "Got an ack for packet "
                   << acked_packet.packet_number;
     last_ack_frame_.packets.Add(acked_packet.packet_number);
-    if (info->largest_acked > kInvalidPacketNumber) {
-      largest_packet_peer_knows_is_acked_ =
-          std::max(largest_packet_peer_knows_is_acked_, info->largest_acked);
+    if (info->largest_acked.IsInitialized()) {
+      if (largest_packet_peer_knows_is_acked_.IsInitialized()) {
+        largest_packet_peer_knows_is_acked_ =
+            std::max(largest_packet_peer_knows_is_acked_, info->largest_acked);
+      } else {
+        largest_packet_peer_knows_is_acked_ = info->largest_acked;
+      }
     }
     // If data is associated with the most recent transmission of this
     // packet, then inform the caller.
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.h b/net/third_party/quic/core/quic_sent_packet_manager.h
index ed0ea8f..0400986a 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager.h
+++ b/net/third_party/quic/core/quic_sent_packet_manager.h
@@ -368,7 +368,9 @@
     LOSS_MODE,
   };
 
-  typedef QuicLinkedHashMap<QuicPacketNumber, TransmissionType>
+  typedef QuicLinkedHashMap<QuicPacketNumber,
+                            TransmissionType,
+                            QuicPacketNumberHash>
       PendingRetransmissionMap;
 
   // Returns the current retransmission mode.
diff --git a/net/third_party/quic/core/quic_sent_packet_manager_test.cc b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
index b3414f2..f41d1fb6 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
@@ -38,7 +38,8 @@
 
 // Matcher to check that the packet number matches the second argument.
 MATCHER(PacketNumberEq, "") {
-  return ::testing::get<0>(arg).packet_number == ::testing::get<1>(arg);
+  return ::testing::get<0>(arg).packet_number ==
+         QuicPacketNumber(::testing::get<1>(arg));
 }
 
 class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate {
@@ -55,23 +56,25 @@
 class QuicSentPacketManagerTest : public QuicTestWithParam<bool> {
  public:
   void RetransmitCryptoPacket(uint64_t packet_number) {
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), packet_number, kDefaultLength,
-                             HAS_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+                     kDefaultLength, HAS_RETRANSMITTABLE_DATA));
     SerializedPacket packet(CreatePacket(packet_number, false));
     packet.retransmittable_frames.push_back(
         QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece())));
     packet.has_crypto_handshake = IS_HANDSHAKE;
-    manager_.OnPacketSent(&packet, 0, clock_.Now(), HANDSHAKE_RETRANSMISSION,
-                          HAS_RETRANSMITTABLE_DATA);
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                          HANDSHAKE_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
   }
 
   void RetransmitDataPacket(uint64_t packet_number, TransmissionType type) {
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), packet_number, kDefaultLength,
-                             HAS_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+                     kDefaultLength, HAS_RETRANSMITTABLE_DATA));
     SerializedPacket packet(CreatePacket(packet_number, true));
-    manager_.OnPacketSent(&packet, 0, clock_.Now(), type,
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), type,
                           HAS_RETRANSMITTABLE_DATA);
   }
 
@@ -110,7 +113,7 @@
   QuicByteCount BytesInFlight() {
     return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_);
   }
-  void VerifyUnackedPackets(QuicPacketNumber* packets, size_t num_packets) {
+  void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) {
     if (num_packets == 0) {
       EXPECT_TRUE(manager_.unacked_packets().empty());
       EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
@@ -119,15 +122,14 @@
     }
 
     EXPECT_FALSE(manager_.unacked_packets().empty());
-    EXPECT_EQ(packets[0], manager_.GetLeastUnacked());
+    EXPECT_EQ(QuicPacketNumber(packets[0]), manager_.GetLeastUnacked());
     for (size_t i = 0; i < num_packets; ++i) {
       EXPECT_TRUE(QuicSentPacketManagerPeer::IsUnacked(&manager_, packets[i]))
           << packets[i];
     }
   }
 
-  void VerifyRetransmittablePackets(QuicPacketNumber* packets,
-                                    size_t num_packets) {
+  void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) {
     EXPECT_EQ(
         num_packets,
         QuicSentPacketManagerPeer::GetNumRetransmittablePackets(&manager_));
@@ -167,17 +169,17 @@
 
   // |packets_acked| and |packets_lost| should be in packet number order.
   void ExpectAcksAndLosses(bool rtt_updated,
-                           QuicPacketNumber* packets_acked,
+                           uint64_t* packets_acked,
                            size_t num_packets_acked,
-                           QuicPacketNumber* packets_lost,
+                           uint64_t* packets_lost,
                            size_t num_packets_lost) {
     std::vector<QuicPacketNumber> ack_vector;
     for (size_t i = 0; i < num_packets_acked; ++i) {
-      ack_vector.push_back(packets_acked[i]);
+      ack_vector.push_back(QuicPacketNumber(packets_acked[i]));
     }
     std::vector<QuicPacketNumber> lost_vector;
     for (size_t i = 0; i < num_packets_lost; ++i) {
-      lost_vector.push_back(packets_lost[i]);
+      lost_vector.push_back(QuicPacketNumber(packets_lost[i]));
     }
     EXPECT_CALL(*send_algorithm_,
                 OnCongestionEvent(rtt_updated, _, _,
@@ -218,26 +220,30 @@
       if (!is_lost) {
         return;
       }
-      EXPECT_CALL(*send_algorithm_,
-                  OnPacketSent(_, BytesInFlight(), new_packet_number,
-                               kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+      EXPECT_CALL(
+          *send_algorithm_,
+          OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number),
+                       kDefaultLength, HAS_RETRANSMITTABLE_DATA));
       SerializedPacket packet(CreatePacket(new_packet_number, true));
-      manager_.OnPacketSent(&packet, 0, clock_.Now(), transmission_type,
-                            HAS_RETRANSMITTABLE_DATA);
+      manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                            transmission_type, HAS_RETRANSMITTABLE_DATA);
       return;
     }
     EXPECT_TRUE(manager_.HasPendingRetransmissions());
     QuicPendingRetransmission next_retransmission =
         manager_.NextPendingRetransmission();
-    EXPECT_EQ(old_packet_number, next_retransmission.packet_number);
+    EXPECT_EQ(QuicPacketNumber(old_packet_number),
+              next_retransmission.packet_number);
     EXPECT_EQ(transmission_type, next_retransmission.transmission_type);
 
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), new_packet_number,
-                             kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number),
+                     kDefaultLength, HAS_RETRANSMITTABLE_DATA));
     SerializedPacket packet(CreatePacket(new_packet_number, false));
-    manager_.OnPacketSent(&packet, old_packet_number, clock_.Now(),
-                          transmission_type, HAS_RETRANSMITTABLE_DATA);
+    manager_.OnPacketSent(&packet, QuicPacketNumber(old_packet_number),
+                          clock_.Now(), transmission_type,
+                          HAS_RETRANSMITTABLE_DATA);
     EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_,
                                                             new_packet_number));
   }
@@ -247,8 +253,9 @@
   }
 
   SerializedPacket CreatePacket(uint64_t packet_number, bool retransmittable) {
-    SerializedPacket packet(packet_number, PACKET_4BYTE_PACKET_NUMBER, nullptr,
-                            kDefaultLength, false, false);
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_4BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
     if (retransmittable) {
       packet.retransmittable_frames.push_back(
           QuicFrame(QuicStreamFrame(kStreamId, false, 0, QuicStringPiece())));
@@ -258,22 +265,24 @@
 
   void SendDataPacket(uint64_t packet_number) {
     EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), packet_number, _, _));
+                OnPacketSent(_, BytesInFlight(),
+                             QuicPacketNumber(packet_number), _, _));
     SerializedPacket packet(CreateDataPacket(packet_number));
-    manager_.OnPacketSent(&packet, 0, clock_.Now(), NOT_RETRANSMISSION,
-                          HAS_RETRANSMITTABLE_DATA);
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                          NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
   }
 
   void SendCryptoPacket(uint64_t packet_number) {
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), packet_number, kDefaultLength,
-                             HAS_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+                     kDefaultLength, HAS_RETRANSMITTABLE_DATA));
     SerializedPacket packet(CreatePacket(packet_number, false));
     packet.retransmittable_frames.push_back(
         QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece())));
     packet.has_crypto_handshake = IS_HANDSHAKE;
-    manager_.OnPacketSent(&packet, 0, clock_.Now(), NOT_RETRANSMISSION,
-                          HAS_RETRANSMITTABLE_DATA);
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                          NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
     if (manager_.session_decides_what_to_write()) {
       EXPECT_CALL(notifier_, HasUnackedCryptoData())
           .WillRepeatedly(Return(true));
@@ -281,21 +290,23 @@
   }
 
   void SendAckPacket(uint64_t packet_number, uint64_t largest_acked) {
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, BytesInFlight(), packet_number, kDefaultLength,
-                             NO_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+                     kDefaultLength, NO_RETRANSMITTABLE_DATA));
     SerializedPacket packet(CreatePacket(packet_number, false));
-    packet.largest_acked = largest_acked;
-    manager_.OnPacketSent(&packet, 0, clock_.Now(), NOT_RETRANSMISSION,
-                          NO_RETRANSMITTABLE_DATA);
+    packet.largest_acked = QuicPacketNumber(largest_acked);
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                          NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
   }
 
   // Based on QuicConnection's WritePendingRetransmissions.
   void RetransmitNextPacket(uint64_t retransmission_packet_number) {
     EXPECT_TRUE(manager_.HasPendingRetransmissions());
-    EXPECT_CALL(*send_algorithm_,
-                OnPacketSent(_, _, retransmission_packet_number, kDefaultLength,
-                             HAS_RETRANSMITTABLE_DATA));
+    EXPECT_CALL(
+        *send_algorithm_,
+        OnPacketSent(_, _, QuicPacketNumber(retransmission_packet_number),
+                     kDefaultLength, HAS_RETRANSMITTABLE_DATA));
     const QuicPendingRetransmission pending =
         manager_.NextPendingRetransmission();
     SerializedPacket packet(CreatePacket(retransmission_packet_number, false));
@@ -317,9 +328,9 @@
   VerifyUnackedPackets(nullptr, 0);
   SendDataPacket(1);
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  QuicPacketNumber retransmittable[] = {1};
+  uint64_t retransmittable[] = {1};
   VerifyRetransmittablePackets(retransmittable,
                                QUIC_ARRAYSIZE(retransmittable));
 }
@@ -329,9 +340,9 @@
   RetransmitAndSendPacket(1, 2);
 
   EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2));
-  QuicPacketNumber unacked[] = {1, 2};
+  uint64_t unacked[] = {1, 2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  std::vector<QuicPacketNumber> retransmittable;
+  std::vector<uint64_t> retransmittable;
   if (manager_.session_decides_what_to_write()) {
     retransmittable = {1, 2};
   } else {
@@ -346,14 +357,15 @@
 
   // Ack 2 but not 1.
   ExpectAck(2);
-  manager_.OnAckFrameStart(2, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(2, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
   }
   // Packet 1 is unacked, pending, but not retransmittable.
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
   VerifyRetransmittablePackets(nullptr, 0);
@@ -376,8 +388,9 @@
   }
   // Ack 1.
   ExpectAck(1);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   // There should no longer be a pending retransmission.
@@ -385,7 +398,7 @@
 
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
-    QuicPacketNumber unacked[] = {2};
+    uint64_t unacked[] = {2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
     // We do not know packet 2 is a spurious retransmission until it gets acked.
   } else {
@@ -415,7 +428,7 @@
   // There should no longer be a pending retransmission.
   EXPECT_FALSE(manager_.HasPendingRetransmissions());
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
   EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
@@ -429,14 +442,15 @@
 
   // Ack 1 but not 2.
   ExpectAck(1);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
   }
   // 2 remains unacked, but no packets have retransmittable data.
-  QuicPacketNumber unacked[] = {2};
+  uint64_t unacked[] = {2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
   VerifyRetransmittablePackets(nullptr, 0);
@@ -444,8 +458,9 @@
     // Ack 2 causes 2 be considered as spurious retransmission.
     EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false));
     ExpectAck(2);
-    manager_.OnAckFrameStart(2, QuicTime::Delta::Infinite(), clock_.Now());
-    manager_.OnAckRange(1, 3);
+    manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                             clock_.Now());
+    manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3));
     EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   }
 
@@ -460,8 +475,9 @@
 
   // First, ACK packet 1 which makes packet 2 non-retransmittable.
   ExpectAck(1);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   SendDataPacket(3);
@@ -471,15 +487,17 @@
 
   // Next, NACK packet 2 three times.
   ExpectAck(3);
-  manager_.OnAckFrameStart(3, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 4);
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   ExpectAck(4);
-  manager_.OnAckFrameStart(4, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 5);
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   ExpectAckAndLoss(true, 5, 2);
@@ -492,14 +510,15 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
     }
   }
-  manager_.OnAckFrameStart(5, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 6);
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   if (manager_.session_decides_what_to_write() &&
       GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-    QuicPacketNumber unacked[] = {2};
+    uint64_t unacked[] = {2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   } else {
     // No packets remain unacked.
@@ -529,12 +548,13 @@
   // send algorithm is not informed that it has been ACK'd.
   ExpectUpdatedRtt(1);
   EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT.
-  QuicPacketNumber unacked[] = {2};
+  uint64_t unacked[] = {2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
   VerifyRetransmittablePackets(nullptr, 0);
@@ -565,8 +585,9 @@
 
   // Ack 1 but not 2 or 3.
   ExpectAck(1);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   if (manager_.session_decides_what_to_write()) {
     // Frames in packets 2 and 3 are acked.
@@ -576,7 +597,7 @@
   }
 
   // 2 and 3 remain unacked, but no packets have retransmittable data.
-  QuicPacketNumber unacked[] = {2, 3};
+  uint64_t unacked[] = {2, 3};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
   VerifyRetransmittablePackets(nullptr, 0);
@@ -589,20 +610,22 @@
         .WillOnce(Return(false))
         .WillRepeatedly(Return(true));
   }
-  QuicPacketNumber acked[] = {3, 4};
+  uint64_t acked[] = {3, 4};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
-  manager_.OnAckFrameStart(4, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 5);
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
-  QuicPacketNumber unacked2[] = {2};
+  uint64_t unacked2[] = {2};
   VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
   EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
 
   SendDataPacket(5);
   ExpectAckAndLoss(true, 5, 2);
-  EXPECT_CALL(debug_delegate, OnPacketLoss(2, LOSS_RETRANSMISSION, _));
+  EXPECT_CALL(debug_delegate,
+              OnPacketLoss(QuicPacketNumber(2), LOSS_RETRANSMISSION, _));
   if (manager_.session_decides_what_to_write()) {
     // Frames in all packets are acked.
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
@@ -612,14 +635,15 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
     }
   }
-  manager_.OnAckFrameStart(5, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 6);
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   if (manager_.session_decides_what_to_write() &&
       GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
-    QuicPacketNumber unacked[] = {2};
+    uint64_t unacked[] = {2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   } else {
     VerifyUnackedPackets(nullptr, 0);
@@ -646,8 +670,9 @@
   {
     ExpectAck(1);
     EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
-    manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-    manager_.OnAckRange(1, 2);
+    manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                             clock_.Now());
+    manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
     EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   }
 
@@ -657,22 +682,25 @@
   {
     ExpectAck(4);
     EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
-    manager_.OnAckFrameStart(4, QuicTime::Delta::Infinite(), clock_.Now());
-    manager_.OnAckRange(4, 5);
-    manager_.OnAckRange(1, 2);
+    manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+                             clock_.Now());
+    manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(5));
+    manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
     EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
     RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
   }
 
   // Ack 3, which causes SpuriousRetransmitDetected to be called.
   {
-    QuicPacketNumber acked[] = {3};
+    uint64_t acked[] = {3};
     ExpectAcksAndLosses(false, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
     EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
-    EXPECT_CALL(*loss_algorithm, SpuriousRetransmitDetected(_, _, _, 5));
-    manager_.OnAckFrameStart(4, QuicTime::Delta::Infinite(), clock_.Now());
-    manager_.OnAckRange(3, 5);
-    manager_.OnAckRange(1, 2);
+    EXPECT_CALL(*loss_algorithm,
+                SpuriousRetransmitDetected(_, _, _, QuicPacketNumber(5)));
+    manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+                             clock_.Now());
+    manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+    manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
     EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
     if (manager_.session_decides_what_to_write()) {
       // Ack 3 will not cause 5 be considered as a spurious retransmission. Ack
@@ -681,57 +709,60 @@
       ExpectAck(5);
       EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
       EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false));
-      manager_.OnAckFrameStart(5, QuicTime::Delta::Infinite(), clock_.Now());
-      manager_.OnAckRange(3, 6);
-      manager_.OnAckRange(1, 2);
+      manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+                               clock_.Now());
+      manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+      manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
       EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
     }
   }
 }
 
 TEST_P(QuicSentPacketManagerTest, GetLeastUnacked) {
-  EXPECT_EQ(1u, manager_.GetLeastUnacked());
+  EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
 }
 
 TEST_P(QuicSentPacketManagerTest, GetLeastUnackedUnacked) {
   SendDataPacket(1);
-  EXPECT_EQ(1u, manager_.GetLeastUnacked());
+  EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
 }
 
 TEST_P(QuicSentPacketManagerTest, AckAckAndUpdateRtt) {
-  EXPECT_EQ(0u, manager_.largest_packet_peer_knows_is_acked());
+  EXPECT_FALSE(manager_.largest_packet_peer_knows_is_acked().IsInitialized());
   SendDataPacket(1);
   SendAckPacket(2, 1);
 
   // Now ack the ack and expect an RTT update.
-  QuicPacketNumber acked[] = {1, 2};
+  uint64_t acked[] = {1, 2};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
-  manager_.OnAckFrameStart(2, QuicTime::Delta::FromMilliseconds(5),
-                           clock_.Now());
-  manager_.OnAckRange(1, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2),
+                           QuicTime::Delta::FromMilliseconds(5), clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
-  EXPECT_EQ(1u, manager_.largest_packet_peer_knows_is_acked());
+  EXPECT_EQ(QuicPacketNumber(1), manager_.largest_packet_peer_knows_is_acked());
 
   SendAckPacket(3, 3);
 
   // Now ack the ack and expect only an RTT update.
-  QuicPacketNumber acked2[] = {3};
+  uint64_t acked2[] = {3};
   ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
-  manager_.OnAckFrameStart(3, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 4);
+  manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
-  EXPECT_EQ(3u, manager_.largest_packet_peer_knows_is_acked());
+  EXPECT_EQ(QuicPacketNumber(3u),
+            manager_.largest_packet_peer_knows_is_acked());
 }
 
 TEST_P(QuicSentPacketManagerTest, Rtt) {
-  QuicPacketNumber packet_number = 1;
   QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(20);
-  SendDataPacket(packet_number);
+  SendDataPacket(1);
   clock_.AdvanceTime(expected_rtt);
 
-  ExpectAck(packet_number);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
 }
@@ -740,15 +771,14 @@
   // Expect that the RTT is equal to the local time elapsed, since the
   // ack_delay_time is larger than the local time elapsed
   // and is hence invalid.
-  QuicPacketNumber packet_number = 1;
   QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
-  SendDataPacket(packet_number);
+  SendDataPacket(1);
   clock_.AdvanceTime(expected_rtt);
 
-  ExpectAck(packet_number);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::FromMilliseconds(11),
-                           clock_.Now());
-  manager_.OnAckRange(1, 2);
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1),
+                           QuicTime::Delta::FromMilliseconds(11), clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
 }
@@ -756,14 +786,14 @@
 TEST_P(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
   // Expect that the RTT is equal to the local time elapsed, since the
   // ack_delay_time is infinite, and is hence invalid.
-  QuicPacketNumber packet_number = 1;
   QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
-  SendDataPacket(packet_number);
+  SendDataPacket(1);
   clock_.AdvanceTime(expected_rtt);
 
-  ExpectAck(packet_number);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
 }
@@ -771,14 +801,14 @@
 TEST_P(QuicSentPacketManagerTest, RttZeroDelta) {
   // Expect that the RTT is the time between send and receive since the
   // ack_delay_time is zero.
-  QuicPacketNumber packet_number = 1;
   QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
-  SendDataPacket(packet_number);
+  SendDataPacket(1);
   clock_.AdvanceTime(expected_rtt);
 
-  ExpectAck(packet_number);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Zero(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Zero(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
 }
@@ -787,8 +817,7 @@
   QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
 
   // Send 1 packet.
-  QuicPacketNumber packet_number = 1;
-  SendDataPacket(packet_number);
+  SendDataPacket(1);
 
   // The first tail loss probe retransmits 1 packet.
   manager_.OnRetransmissionTimeout();
@@ -827,8 +856,9 @@
   // Ack the third and ensure the first two are still pending.
   ExpectAck(3);
 
-  manager_.OnAckFrameStart(3, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 4);
+  manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
@@ -836,8 +866,8 @@
   // Acking two more packets will lose both of them due to nacks.
   SendDataPacket(4);
   SendDataPacket(5);
-  QuicPacketNumber acked[] = {4, 5};
-  QuicPacketNumber lost[] = {1, 2};
+  uint64_t acked[] = {4, 5};
+  uint64_t lost[] = {1, 2};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), lost,
                       QUIC_ARRAYSIZE(lost));
   if (manager_.session_decides_what_to_write()) {
@@ -849,8 +879,9 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2);
     }
   }
-  manager_.OnAckFrameStart(5, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 6);
+  manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   EXPECT_FALSE(manager_.HasPendingRetransmissions());
@@ -942,7 +973,7 @@
     EXPECT_TRUE(manager_.HasPendingRetransmissions());
     RetransmitNextPacket(103);
   }
-  QuicPacketNumber largest_acked = 103;
+  QuicPacketNumber largest_acked = QuicPacketNumber(103);
   EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
   EXPECT_CALL(*send_algorithm_,
               OnCongestionEvent(
@@ -971,8 +1002,9 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(101);
     }
   }
-  manager_.OnAckFrameStart(103, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(103, 104);
+  manager_.OnAckFrameStart(QuicPacketNumber(103), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(103), QuicPacketNumber(104));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   // All packets before 103 should be lost.
   if (manager_.session_decides_what_to_write()) {
@@ -1029,15 +1061,16 @@
 
   // Now ack the two crypto packets and the speculatively encrypted request,
   // and ensure the first four crypto packets get abandoned, but not lost.
-  QuicPacketNumber acked[] = {3, 4, 5, 8, 9};
+  uint64_t acked[] = {3, 4, 5, 8, 9};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, HasUnackedCryptoData())
         .WillRepeatedly(Return(false));
   }
-  manager_.OnAckFrameStart(9, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(8, 10);
-  manager_.OnAckRange(3, 6);
+  manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10));
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
@@ -1087,9 +1120,11 @@
     RetransmitDataPacket(12, ALL_UNACKED_RETRANSMISSION);
   } else {
     ASSERT_TRUE(manager_.HasPendingRetransmissions());
-    EXPECT_EQ(6u, manager_.NextPendingRetransmission().packet_number);
+    EXPECT_EQ(QuicPacketNumber(6u),
+              manager_.NextPendingRetransmission().packet_number);
     RetransmitNextPacket(8);
-    EXPECT_EQ(7u, manager_.NextPendingRetransmission().packet_number);
+    EXPECT_EQ(QuicPacketNumber(7u),
+              manager_.NextPendingRetransmission().packet_number);
     RetransmitNextPacket(9);
     EXPECT_TRUE(manager_.HasPendingRetransmissions());
     // Send 3 more data packets and ensure the least unacked is raised.
@@ -1099,19 +1134,20 @@
     EXPECT_FALSE(manager_.HasPendingRetransmissions());
   }
 
-  EXPECT_EQ(1u, manager_.GetLeastUnacked());
+  EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
   // Least unacked isn't raised until an ack is received, so ack the
   // crypto packets.
-  QuicPacketNumber acked[] = {8, 9};
+  uint64_t acked[] = {8, 9};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
-  manager_.OnAckFrameStart(9, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(8, 10);
+  manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, HasUnackedCryptoData())
         .WillRepeatedly(Return(false));
   }
-  EXPECT_EQ(10u, manager_.GetLeastUnacked());
+  EXPECT_EQ(QuicPacketNumber(10u), manager_.GetLeastUnacked());
 }
 
 TEST_P(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) {
@@ -1141,19 +1177,20 @@
 
   // Now ack the second crypto packet, and ensure the first gets removed, but
   // the third does not.
-  QuicPacketNumber acked[] = {2};
+  uint64_t acked[] = {2};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
   if (manager_.session_decides_what_to_write()) {
     EXPECT_CALL(notifier_, HasUnackedCryptoData())
         .WillRepeatedly(Return(false));
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
   }
-  manager_.OnAckFrameStart(2, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(2, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
-  QuicPacketNumber unacked[] = {3};
+  uint64_t unacked[] = {3};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
 }
 
@@ -1211,7 +1248,7 @@
   } else {
     // Packet 2 is useful because it does not get retransmitted and still has
     // retransmittable frames.
-    QuicPacketNumber unacked[] = {1, 2};
+    uint64_t unacked[] = {1, 2};
     VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
     EXPECT_TRUE(manager_.HasPendingRetransmissions());
   }
@@ -1257,7 +1294,7 @@
     EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
   }
   EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
-  QuicPacketNumber unacked[] = {1, 2, 3};
+  uint64_t unacked[] = {1, 2, 3};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
   EXPECT_FALSE(manager_.HasPendingRetransmissions());
@@ -1265,10 +1302,11 @@
   EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
 
   // Ensure both packets get discarded when packet 2 is acked.
-  QuicPacketNumber acked[] = {3};
+  uint64_t acked[] = {3};
   ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
-  manager_.OnAckFrameStart(3, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(3, 4);
+  manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   VerifyUnackedPackets(nullptr, 0);
   VerifyRetransmittablePackets(nullptr, 0);
@@ -1311,7 +1349,7 @@
 
   // Ack a retransmission.
   // Ensure no packets are lost.
-  QuicPacketNumber largest_acked = 102;
+  QuicPacketNumber largest_acked = QuicPacketNumber(102);
   EXPECT_CALL(*send_algorithm_,
               OnCongestionEvent(true, _, _,
                                 Pointwise(PacketNumberEq(), {largest_acked}),
@@ -1321,7 +1359,8 @@
   // RTO's use loss detection instead of immediately declaring retransmitted
   // packets lost.
   for (int i = 1; i <= 99; ++i) {
-    EXPECT_CALL(debug_delegate, OnPacketLoss(i, LOSS_RETRANSMISSION, _));
+    EXPECT_CALL(debug_delegate,
+                OnPacketLoss(QuicPacketNumber(i), LOSS_RETRANSMISSION, _));
   }
   if (manager_.session_decides_what_to_write()) {
     if (GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
@@ -1346,8 +1385,9 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(98);
     }
   }
-  manager_.OnAckFrameStart(102, QuicTime::Delta::Zero(), clock_.Now());
-  manager_.OnAckRange(102, 103);
+  manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 }
 
@@ -1445,7 +1485,7 @@
 
   // Ack a retransmission and expect no call to OnRetransmissionTimeout.
   // This will include packets in the lost packet map.
-  QuicPacketNumber largest_acked = 102;
+  QuicPacketNumber largest_acked = QuicPacketNumber(102);
   EXPECT_CALL(*send_algorithm_,
               OnCongestionEvent(true, _, _,
                                 Pointwise(PacketNumberEq(), {largest_acked}),
@@ -1474,8 +1514,9 @@
       EXPECT_CALL(notifier_, OnFrameLost(_)).Times(98);
     }
   }
-  manager_.OnAckFrameStart(102, QuicTime::Delta::Zero(), clock_.Now());
-  manager_.OnAckRange(102, 103);
+  manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 }
 
@@ -1521,8 +1562,9 @@
   // Ack a retransmission and ensure OnRetransmissionTimeout is called.
   EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
   ExpectAck(2);
-  manager_.OnAckFrameStart(2, QuicTime::Delta::Zero(), clock_.Now());
-  manager_.OnAckRange(2, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Zero(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   // The original packet and newest should be outstanding.
@@ -1572,8 +1614,9 @@
   // Ack a retransmission and ensure OnRetransmissionTimeout is called.
   EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
   ExpectAck(3);
-  manager_.OnAckFrameStart(3, QuicTime::Delta::Zero(), clock_.Now());
-  manager_.OnAckRange(3, 4);
+  manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Zero(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   // The first two packets should still be outstanding.
@@ -1773,8 +1816,9 @@
   // Ack a packet before the first RTO and ensure the RTO timeout returns to the
   // original value and OnRetransmissionTimeout is not called or reverted.
   ExpectAck(2);
-  manager_.OnAckFrameStart(2, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(2, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
   EXPECT_FALSE(manager_.HasPendingRetransmissions());
   EXPECT_EQ(5 * kDefaultLength,
@@ -1907,8 +1951,9 @@
   // set the loss timeout.
   ExpectAck(2);
   EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
-  manager_.OnAckFrameStart(2, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(2, 3);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   QuicTime timeout(clock_.Now() + QuicTime::Delta::FromMilliseconds(10));
@@ -2419,19 +2464,21 @@
 }
 
 TEST_P(QuicSentPacketManagerTest, PathMtuIncreased) {
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, BytesInFlight(), 1, _, _));
-  SerializedPacket packet(1, PACKET_4BYTE_PACKET_NUMBER, nullptr,
-                          kDefaultLength + 100, false, false);
-  manager_.OnPacketSent(&packet, 0, clock_.Now(), NOT_RETRANSMISSION,
-                        HAS_RETRANSMITTABLE_DATA);
+  EXPECT_CALL(*send_algorithm_,
+              OnPacketSent(_, BytesInFlight(), QuicPacketNumber(1), _, _));
+  SerializedPacket packet(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
+                          nullptr, kDefaultLength + 100, false, false);
+  manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                        NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
 
   // Ack the large packet and expect the path MTU to increase.
   ExpectAck(1);
   EXPECT_CALL(*network_change_visitor_,
               OnPathMtuIncreased(kDefaultLength + 100));
   QuicAckFrame ack_frame = InitAckFrame(1);
-  manager_.OnAckFrameStart(1, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(1, 2);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 }
 
@@ -2441,26 +2488,28 @@
     SendDataPacket(i);
   }
   // Ack [5, 7), [10, 12), [15, 17).
-  QuicPacketNumber acked1[] = {5, 6, 10, 11, 15, 16};
-  QuicPacketNumber lost1[] = {1, 2, 3, 4, 7, 8, 9, 12, 13};
+  uint64_t acked1[] = {5, 6, 10, 11, 15, 16};
+  uint64_t lost1[] = {1, 2, 3, 4, 7, 8, 9, 12, 13};
   ExpectAcksAndLosses(true, acked1, QUIC_ARRAYSIZE(acked1), lost1,
                       QUIC_ARRAYSIZE(lost1));
   EXPECT_CALL(notifier_, OnFrameLost(_)).Times(AnyNumber());
-  manager_.OnAckFrameStart(16, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(15, 17);
-  manager_.OnAckRange(10, 12);
-  manager_.OnAckRange(5, 7);
+  manager_.OnAckFrameStart(QuicPacketNumber(16), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(15), QuicPacketNumber(17));
+  manager_.OnAckRange(QuicPacketNumber(10), QuicPacketNumber(12));
+  manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(7));
   // Make sure empty range does not harm.
-  manager_.OnAckRange(4, 4);
+  manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(4));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 
   // Ack [4, 8), [9, 13), [14, 21).
-  QuicPacketNumber acked2[] = {4, 7, 9, 12, 14, 17, 18, 19, 20};
+  uint64_t acked2[] = {4, 7, 9, 12, 14, 17, 18, 19, 20};
   ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
-  manager_.OnAckFrameStart(20, QuicTime::Delta::Infinite(), clock_.Now());
-  manager_.OnAckRange(14, 21);
-  manager_.OnAckRange(9, 13);
-  manager_.OnAckRange(4, 8);
+  manager_.OnAckFrameStart(QuicPacketNumber(20), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(14), QuicPacketNumber(21));
+  manager_.OnAckRange(QuicPacketNumber(9), QuicPacketNumber(13));
+  manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(8));
   EXPECT_TRUE(manager_.OnAckFrameEnd(clock_.Now()));
 }
 
diff --git a/net/third_party/quic/core/quic_time_wait_list_manager_test.cc b/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
index 42fdc34d6..762548ff 100644
--- a/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
+++ b/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
@@ -185,7 +185,7 @@
   QuicEncryptedPacket* ConstructEncryptedPacket(
       QuicConnectionId destination_connection_id,
       QuicConnectionId source_connection_id,
-      QuicPacketNumber packet_number) {
+      uint64_t packet_number) {
     return quic::test::ConstructEncryptedPacket(destination_connection_id,
                                                 source_connection_id, false,
                                                 false, packet_number, "data");
@@ -406,9 +406,8 @@
   QuicConnectionId connection_id = TestConnectionId(1);
   EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id));
   AddConnectionId(connection_id, QuicTimeWaitListManager::SEND_STATELESS_RESET);
-  QuicPacketNumber packet_number = 234;
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
-      connection_id, EmptyQuicConnectionId(), packet_number));
+      connection_id, EmptyQuicConnectionId(), /*packet_number=*/234));
   // Let first write through.
   EXPECT_CALL(writer_,
               WritePacket(_, _, self_address_.host(), peer_address_, _))
@@ -433,9 +432,8 @@
   EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(other_connection_id));
   AddConnectionId(other_connection_id,
                   QuicTimeWaitListManager::SEND_STATELESS_RESET);
-  QuicPacketNumber other_packet_number = 23423;
   std::unique_ptr<QuicEncryptedPacket> other_packet(ConstructEncryptedPacket(
-      other_connection_id, EmptyQuicConnectionId(), other_packet_number));
+      other_connection_id, EmptyQuicConnectionId(), /*packet_number=*/23423));
   EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0);
   EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_));
   ProcessPacket(other_connection_id);
diff --git a/net/third_party/quic/core/quic_trace_visitor.cc b/net/third_party/quic/core/quic_trace_visitor.cc
index c597fa86..2b30bc72 100644
--- a/net/third_party/quic/core/quic_trace_visitor.cc
+++ b/net/third_party/quic/core/quic_trace_visitor.cc
@@ -59,7 +59,7 @@
   quic_trace::Event* event = trace_.add_events();
   event->set_event_type(quic_trace::PACKET_SENT);
   event->set_time_us(ConvertTimestampToRecordedFormat(sent_time));
-  event->set_packet_number(serialized_packet.packet_number);
+  event->set_packet_number(serialized_packet.packet_number.ToUint64());
   event->set_packet_size(serialized_packet.encrypted_length);
   event->set_encryption_level(
       EncryptionLevelToProto(serialized_packet.encryption_level));
@@ -140,8 +140,8 @@
         quic_trace::AckBlock* block = info->add_acked_packets();
         // We record intervals as [a, b], whereas the in-memory representation
         // we currently use is [a, b).
-        block->set_first_packet(interval.min());
-        block->set_last_packet(interval.max() - 1);
+        block->set_first_packet(interval.min().ToUint64());
+        block->set_last_packet(interval.max().ToUint64() - 1);
       }
       break;
     }
@@ -240,7 +240,7 @@
   quic_trace::Event* event = trace_.add_events();
   event->set_time_us(ConvertTimestampToRecordedFormat(ack_receive_time));
   event->set_packet_number(
-      connection_->received_packet_manager().GetLargestObserved());
+      connection_->received_packet_manager().GetLargestObserved().ToUint64());
   event->set_event_type(quic_trace::PACKET_RECEIVED);
 
   // TODO(vasilvv): consider removing this copy.
@@ -255,7 +255,7 @@
   quic_trace::Event* event = trace_.add_events();
   event->set_time_us(ConvertTimestampToRecordedFormat(detection_time));
   event->set_event_type(quic_trace::PACKET_LOST);
-  event->set_packet_number(lost_packet_number);
+  event->set_packet_number(lost_packet_number.ToUint64());
   PopulateTransportState(event->mutable_transport_state());
 }
 
@@ -265,7 +265,7 @@
   event->set_time_us(ConvertTimestampToRecordedFormat(receive_time));
   event->set_event_type(quic_trace::PACKET_RECEIVED);
   event->set_packet_number(
-      connection_->received_packet_manager().GetLargestObserved());
+      connection_->received_packet_manager().GetLargestObserved().ToUint64());
 
   // TODO(vasilvv): consider removing this copy.
   QuicWindowUpdateFrame copy_of_update = frame;
diff --git a/net/third_party/quic/core/quic_trace_visitor_test.cc b/net/third_party/quic/core/quic_trace_visitor_test.cc
index b78dbab5..e5203d622 100644
--- a/net/third_party/quic/core/quic_trace_visitor_test.cc
+++ b/net/third_party/quic/core/quic_trace_visitor_test.cc
@@ -135,20 +135,22 @@
 
         const quic_trace::AckInfo& info = frame.ack_info();
         for (const auto& block : info.acked_packets()) {
-          packets.Add(block.first_packet(), block.last_packet() + 1);
+          packets.Add(QuicPacketNumber(block.first_packet()),
+                      QuicPacketNumber(block.last_packet()) + 1);
         }
       }
     }
     if (packet.event_type() == quic_trace::PACKET_LOST) {
-      packets.Add(packet.packet_number(), packet.packet_number() + 1);
+      packets.Add(QuicPacketNumber(packet.packet_number()),
+                  QuicPacketNumber(packet.packet_number()) + 1);
     }
   }
 
   ASSERT_EQ(1u, packets.Size());
-  EXPECT_EQ(1u, packets.begin()->min());
+  EXPECT_EQ(QuicPacketNumber(1u), packets.begin()->min());
   // We leave some room (20 packets) for the packets which did not receive
   // conclusive status at the end of simulation.
-  EXPECT_GT(packets.rbegin()->max(), packets_sent_ - 20);
+  EXPECT_GT(packets.rbegin()->max(), QuicPacketNumber(packets_sent_ - 20));
 }
 
 TEST_F(QuicTraceVisitorTest, TransportState) {
diff --git a/net/third_party/quic/core/quic_transmission_info.cc b/net/third_party/quic/core/quic_transmission_info.cc
index 0ea2443..c8e502e 100644
--- a/net/third_party/quic/core/quic_transmission_info.cc
+++ b/net/third_party/quic/core/quic_transmission_info.cc
@@ -15,9 +15,7 @@
       in_flight(false),
       state(OUTSTANDING),
       has_crypto_handshake(false),
-      num_padding_bytes(0),
-      retransmission(kInvalidPacketNumber),
-      largest_acked(kInvalidPacketNumber) {}
+      num_padding_bytes(0) {}
 
 QuicTransmissionInfo::QuicTransmissionInfo(
     EncryptionLevel level,
@@ -35,9 +33,7 @@
       in_flight(false),
       state(OUTSTANDING),
       has_crypto_handshake(has_crypto_handshake),
-      num_padding_bytes(num_padding_bytes),
-      retransmission(kInvalidPacketNumber),
-      largest_acked(kInvalidPacketNumber) {}
+      num_padding_bytes(num_padding_bytes) {}
 
 QuicTransmissionInfo::QuicTransmissionInfo(const QuicTransmissionInfo& other) =
     default;
diff --git a/net/third_party/quic/core/quic_types.h b/net/third_party/quic/core/quic_types.h
index 5f3e02cc..11bbcb4 100644
--- a/net/third_party/quic/core/quic_types.h
+++ b/net/third_party/quic/core/quic_types.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "net/third_party/quic/core/quic_connection_id.h"
+#include "net/third_party/quic/core/quic_packet_number.h"
 #include "net/third_party/quic/core/quic_time.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 
@@ -27,7 +28,6 @@
 
 typedef uint64_t QuicByteCount;
 typedef uint64_t QuicPacketCount;
-typedef uint64_t QuicPacketNumber;
 typedef uint64_t QuicPublicResetNonceProof;
 typedef uint64_t QuicStreamOffset;
 typedef std::array<char, 32> DiversificationNonce;
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.cc b/net/third_party/quic/core/quic_unacked_packet_map.cc
index 7c46bfac0..224cf95 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map.cc
@@ -25,11 +25,7 @@
 }  // namespace
 
 QuicUnackedPacketMap::QuicUnackedPacketMap()
-    : largest_sent_packet_(0),
-      largest_sent_retransmittable_packet_(0),
-      largest_sent_largest_acked_(0),
-      largest_acked_(0),
-      least_unacked_(kFirstSendingPacketNumber),
+    : least_unacked_(FirstSendingPacketNumber()),
       bytes_in_flight_(0),
       pending_crypto_packet_count_(0),
       last_crypto_packet_sent_time_(QuicTime::Zero()),
@@ -49,7 +45,10 @@
                                          bool set_in_flight) {
   QuicPacketNumber packet_number = packet->packet_number;
   QuicPacketLength bytes_sent = packet->encrypted_length;
-  QUIC_BUG_IF(largest_sent_packet_ >= packet_number) << packet_number;
+  QUIC_BUG_IF(largest_sent_packet_.IsInitialized() &&
+              largest_sent_packet_ >= packet_number)
+      << "largest_sent_packet_: " << largest_sent_packet_
+      << ", packet_number: " << packet_number;
   DCHECK_GE(packet_number, least_unacked_ + unacked_packets_.size());
   while (least_unacked_ + unacked_packets_.size() < packet_number) {
     unacked_packets_.push_back(QuicTransmissionInfo());
@@ -62,9 +61,13 @@
       packet->encryption_level, packet->packet_number_length, transmission_type,
       sent_time, bytes_sent, has_crypto_handshake, packet->num_padding_bytes);
   info.largest_acked = packet->largest_acked;
-  largest_sent_largest_acked_ =
-      std::max(largest_sent_largest_acked_, packet->largest_acked);
-  if (old_packet_number > 0) {
+  if (packet->largest_acked.IsInitialized()) {
+    largest_sent_largest_acked_ =
+        largest_sent_largest_acked_.IsInitialized()
+            ? std::max(largest_sent_largest_acked_, packet->largest_acked)
+            : packet->largest_acked;
+  }
+  if (old_packet_number.IsInitialized()) {
     TransferRetransmissionInfo(old_packet_number, packet_number,
                                transmission_type, &info);
   }
@@ -78,7 +81,7 @@
   unacked_packets_.push_back(info);
   // Swap the retransmittable frames to avoid allocations.
   // TODO(ianswett): Could use emplace_back when Chromium can.
-  if (old_packet_number == kInvalidPacketNumber) {
+  if (!old_packet_number.IsInitialized()) {
     if (has_crypto_handshake) {
       ++pending_crypto_packet_count_;
       last_crypto_packet_sent_time_ = sent_time;
@@ -180,12 +183,12 @@
     QuicTransmissionInfo* info) {
   if (session_decides_what_to_write_) {
     DeleteFrames(&info->retransmittable_frames);
-    info->retransmission = kInvalidPacketNumber;
+    info->retransmission.Clear();
     return;
   }
-  while (info->retransmission != kInvalidPacketNumber) {
+  while (info->retransmission.IsInitialized()) {
     const QuicPacketNumber retransmission = info->retransmission;
-    info->retransmission = kInvalidPacketNumber;
+    info->retransmission.Clear();
     info = &unacked_packets_[retransmission - least_unacked_];
   }
 
@@ -209,7 +212,7 @@
 
 void QuicUnackedPacketMap::IncreaseLargestAcked(
     QuicPacketNumber largest_acked) {
-  DCHECK_LE(largest_acked_, largest_acked);
+  DCHECK(!largest_acked_.IsInitialized() || largest_acked_ <= largest_acked);
   largest_acked_ = largest_acked;
 }
 
@@ -218,7 +221,8 @@
     const QuicTransmissionInfo& info) const {
   // Packet can be used for RTT measurement if it may yet be acked as the
   // largest observed packet by the receiver.
-  return QuicUtils::IsAckable(info.state) && packet_number > largest_acked_;
+  return QuicUtils::IsAckable(info.state) &&
+         (!largest_acked_.IsInitialized() || packet_number > largest_acked_);
 }
 
 bool QuicUnackedPacketMap::IsPacketUsefulForCongestionControl(
@@ -233,15 +237,16 @@
     // Packet may have retransmittable frames, or the data may have been
     // retransmitted with a new packet number.
     // Allow for an extra 1 RTT before stopping to track old packets.
-    return info.retransmission > largest_acked_ ||
+    return (info.retransmission.IsInitialized() &&
+            (!largest_acked_.IsInitialized() ||
+             info.retransmission > largest_acked_)) ||
            HasRetransmittableFrames(info);
   }
 
   // Wait for 1 RTT before giving up on the lost packet.
-  if (info.retransmission > largest_acked_) {
-    return true;
-  }
-  return false;
+  return info.retransmission.IsInitialized() &&
+         (!largest_acked_.IsInitialized() ||
+          info.retransmission > largest_acked_);
 }
 
 bool QuicUnackedPacketMap::IsPacketUseless(
@@ -472,7 +477,7 @@
 
 void QuicUnackedPacketMap::SetSessionDecideWhatToWrite(
     bool session_decides_what_to_write) {
-  if (largest_sent_packet_ > 0) {
+  if (largest_sent_packet_.IsInitialized()) {
     QUIC_BUG << "Cannot change session_decide_what_to_write with packets sent.";
     return;
   }
diff --git a/net/third_party/quic/core/quic_unacked_packet_map_test.cc b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
index b33b065..fb836fd3 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map_test.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
@@ -46,30 +46,31 @@
 
   ~QuicUnackedPacketMapTest() override {}
 
-  SerializedPacket CreateRetransmittablePacket(QuicPacketNumber packet_number) {
+  SerializedPacket CreateRetransmittablePacket(uint64_t packet_number) {
     return CreateRetransmittablePacketForStream(
         packet_number, QuicUtils::GetHeadersStreamId(
                            CurrentSupportedVersions()[0].transport_version));
   }
 
   SerializedPacket CreateRetransmittablePacketForStream(
-      QuicPacketNumber packet_number,
+      uint64_t packet_number,
       QuicStreamId stream_id) {
-    SerializedPacket packet(packet_number, PACKET_1BYTE_PACKET_NUMBER, nullptr,
-                            kDefaultLength, false, false);
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
     QuicStreamFrame frame;
     frame.stream_id = stream_id;
     packet.retransmittable_frames.push_back(QuicFrame(frame));
     return packet;
   }
 
-  SerializedPacket CreateNonRetransmittablePacket(
-      QuicPacketNumber packet_number) {
-    return SerializedPacket(packet_number, PACKET_1BYTE_PACKET_NUMBER, nullptr,
-                            kDefaultLength, false, false);
+  SerializedPacket CreateNonRetransmittablePacket(uint64_t packet_number) {
+    return SerializedPacket(QuicPacketNumber(packet_number),
+                            PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
   }
 
-  void VerifyInFlightPackets(QuicPacketNumber* packets, size_t num_packets) {
+  void VerifyInFlightPackets(uint64_t* packets, size_t num_packets) {
     unacked_packets_.RemoveObsoletePackets();
     if (num_packets == 0) {
       EXPECT_FALSE(unacked_packets_.HasInFlightPackets());
@@ -79,12 +80,16 @@
     if (num_packets == 1) {
       EXPECT_TRUE(unacked_packets_.HasInFlightPackets());
       EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
-      ASSERT_TRUE(unacked_packets_.IsUnacked(packets[0]));
-      EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[0]).in_flight);
+      ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[0])));
+      EXPECT_TRUE(
+          unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[0]))
+              .in_flight);
     }
     for (size_t i = 0; i < num_packets; ++i) {
-      ASSERT_TRUE(unacked_packets_.IsUnacked(packets[i]));
-      EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[i]).in_flight);
+      ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i])));
+      EXPECT_TRUE(
+          unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[i]))
+              .in_flight);
     }
     size_t in_flight_count = 0;
     for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
@@ -96,7 +101,7 @@
     EXPECT_EQ(num_packets, in_flight_count);
   }
 
-  void VerifyUnackedPackets(QuicPacketNumber* packets, size_t num_packets) {
+  void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) {
     unacked_packets_.RemoveObsoletePackets();
     if (num_packets == 0) {
       EXPECT_TRUE(unacked_packets_.empty());
@@ -107,13 +112,13 @@
     }
     EXPECT_FALSE(unacked_packets_.empty());
     for (size_t i = 0; i < num_packets; ++i) {
-      EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i];
+      EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i])))
+          << packets[i];
     }
     EXPECT_EQ(num_packets, unacked_packets_.GetNumUnackedPacketsDebugOnly());
   }
 
-  void VerifyRetransmittablePackets(QuicPacketNumber* packets,
-                                    size_t num_packets) {
+  void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) {
     unacked_packets_.RemoveObsoletePackets();
     size_t num_retransmittable_packets = 0;
     for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
@@ -124,29 +129,33 @@
     }
     EXPECT_EQ(num_packets, num_retransmittable_packets);
     for (size_t i = 0; i < num_packets; ++i) {
-      EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(packets[i]))
+      EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(
+          QuicPacketNumber(packets[i])))
           << " packets[" << i << "]:" << packets[i];
     }
   }
 
-  void UpdatePacketState(QuicPacketNumber packet_number,
-                         SentPacketState state) {
-    unacked_packets_.GetMutableTransmissionInfo(packet_number)->state = state;
+  void UpdatePacketState(uint64_t packet_number, SentPacketState state) {
+    unacked_packets_
+        .GetMutableTransmissionInfo(QuicPacketNumber(packet_number))
+        ->state = state;
   }
 
   void RetransmitAndSendPacket(uint64_t old_packet_number,
                                uint64_t new_packet_number,
                                TransmissionType transmission_type) {
-    DCHECK(unacked_packets_.HasRetransmittableFrames(old_packet_number));
+    DCHECK(unacked_packets_.HasRetransmittableFrames(
+        QuicPacketNumber(old_packet_number)));
     if (!unacked_packets_.session_decides_what_to_write()) {
       SerializedPacket packet(
           CreateNonRetransmittablePacket(new_packet_number));
-      unacked_packets_.AddSentPacket(&packet, old_packet_number,
+      unacked_packets_.AddSentPacket(&packet,
+                                     QuicPacketNumber(old_packet_number),
                                      transmission_type, now_, true);
       return;
     }
-    QuicTransmissionInfo* info =
-        unacked_packets_.GetMutableTransmissionInfo(old_packet_number);
+    QuicTransmissionInfo* info = unacked_packets_.GetMutableTransmissionInfo(
+        QuicPacketNumber(old_packet_number));
     QuicStreamId stream_id = QuicUtils::GetHeadersStreamId(
         CurrentSupportedVersions()[0].transport_version);
     for (const auto& frame : info->retransmittable_frames) {
@@ -158,10 +167,11 @@
     UpdatePacketState(
         old_packet_number,
         QuicUtils::RetransmissionTypeToPacketState(transmission_type));
-    info->retransmission = new_packet_number;
+    info->retransmission = QuicPacketNumber(new_packet_number);
     SerializedPacket packet(
         CreateRetransmittablePacketForStream(new_packet_number, stream_id));
-    unacked_packets_.AddSentPacket(&packet, 0, transmission_type, now_, true);
+    unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                   transmission_type, now_, true);
   }
   QuicUnackedPacketMap unacked_packets_;
   QuicTime now_;
@@ -173,14 +183,15 @@
 TEST_P(QuicUnackedPacketMapTest, RttOnly) {
   // Acks are only tracked for RTT measurement purposes.
   SerializedPacket packet(CreateNonRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, now_, false);
+  unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, false);
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(nullptr, 0);
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.IncreaseLargestAcked(1);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1));
   VerifyUnackedPackets(nullptr, 0);
   VerifyInFlightPackets(nullptr, 0);
   VerifyRetransmittablePackets(nullptr, 0);
@@ -189,24 +200,25 @@
 TEST_P(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) {
   // Simulate a retransmittable packet being sent and acked.
   SerializedPacket packet(CreateRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(unacked, QUIC_ARRAYSIZE(unacked));
 
-  unacked_packets_.RemoveRetransmittability(1);
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.IncreaseLargestAcked(1);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1));
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.RemoveFromInFlight(1);
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
   VerifyUnackedPackets(nullptr, 0);
   VerifyInFlightPackets(nullptr, 0);
   VerifyRetransmittablePackets(nullptr, 0);
@@ -215,12 +227,13 @@
 TEST_P(QuicUnackedPacketMapTest, StopRetransmission) {
   const QuicStreamId stream_id = 2;
   SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id));
-  unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  QuicPacketNumber retransmittable[] = {1};
+  uint64_t retransmittable[] = {1};
   VerifyRetransmittablePackets(retransmittable,
                                QUIC_ARRAYSIZE(retransmittable));
 
@@ -237,12 +250,13 @@
 TEST_P(QuicUnackedPacketMapTest, StopRetransmissionOnOtherStream) {
   const QuicStreamId stream_id = 2;
   SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id));
-  unacked_packets_.AddSentPacket(&packet, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked[] = {1};
+  uint64_t unacked[] = {1};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  QuicPacketNumber retransmittable[] = {1};
+  uint64_t retransmittable[] = {1};
   VerifyRetransmittablePackets(retransmittable,
                                QUIC_ARRAYSIZE(retransmittable));
 
@@ -259,13 +273,14 @@
 TEST_P(QuicUnackedPacketMapTest, StopRetransmissionAfterRetransmission) {
   const QuicStreamId stream_id = 2;
   SerializedPacket packet1(CreateRetransmittablePacketForStream(1, stream_id));
-  unacked_packets_.AddSentPacket(&packet1, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION);
 
-  QuicPacketNumber unacked[] = {1, 2};
+  uint64_t unacked[] = {1, 2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  std::vector<QuicPacketNumber> retransmittable;
+  std::vector<uint64_t> retransmittable;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable = {1, 2};
   } else {
@@ -287,13 +302,14 @@
   // Simulate a retransmittable packet being sent, retransmitted, and the first
   // transmission being acked.
   SerializedPacket packet1(CreateRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet1, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION);
 
-  QuicPacketNumber unacked[] = {1, 2};
+  uint64_t unacked[] = {1, 2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  std::vector<QuicPacketNumber> retransmittable;
+  std::vector<uint64_t> retransmittable;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable = {1, 2};
   } else {
@@ -302,23 +318,23 @@
   VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size());
 
   EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
-  unacked_packets_.RemoveRetransmittability(1);
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.IncreaseLargestAcked(2);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.RemoveFromInFlight(2);
-  QuicPacketNumber unacked2[] = {1};
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  uint64_t unacked2[] = {1};
   VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
   VerifyInFlightPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
   VerifyRetransmittablePackets(nullptr, 0);
 
-  unacked_packets_.RemoveFromInFlight(1);
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
   VerifyUnackedPackets(nullptr, 0);
   VerifyInFlightPackets(nullptr, 0);
   VerifyRetransmittablePackets(nullptr, 0);
@@ -327,31 +343,34 @@
 TEST_P(QuicUnackedPacketMapTest, RetransmitThreeTimes) {
   // Simulate a retransmittable packet being sent and retransmitted twice.
   SerializedPacket packet1(CreateRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet1, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   SerializedPacket packet2(CreateRetransmittablePacket(2));
-  unacked_packets_.AddSentPacket(&packet2, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked[] = {1, 2};
+  uint64_t unacked[] = {1, 2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  QuicPacketNumber retransmittable[] = {1, 2};
+  uint64_t retransmittable[] = {1, 2};
   VerifyRetransmittablePackets(retransmittable,
                                QUIC_ARRAYSIZE(retransmittable));
 
   // Early retransmit 1 as 3 and send new data as 4.
-  unacked_packets_.IncreaseLargestAcked(2);
-  unacked_packets_.RemoveFromInFlight(2);
-  unacked_packets_.RemoveRetransmittability(2);
-  unacked_packets_.RemoveFromInFlight(1);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
   RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION);
   SerializedPacket packet4(CreateRetransmittablePacket(4));
-  unacked_packets_.AddSentPacket(&packet4, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet4, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked2[] = {1, 3, 4};
+  uint64_t unacked2[] = {1, 3, 4};
   VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
-  QuicPacketNumber pending2[] = {3, 4};
+  uint64_t pending2[] = {3, 4};
   VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2));
-  std::vector<QuicPacketNumber> retransmittable2;
+  std::vector<uint64_t> retransmittable2;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable2 = {1, 3, 4};
   } else {
@@ -360,15 +379,16 @@
   VerifyRetransmittablePackets(&retransmittable2[0], retransmittable2.size());
 
   // Early retransmit 3 (formerly 1) as 5, and remove 1 from unacked.
-  unacked_packets_.IncreaseLargestAcked(4);
-  unacked_packets_.RemoveFromInFlight(4);
-  unacked_packets_.RemoveRetransmittability(4);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(4));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(4));
   RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
   SerializedPacket packet6(CreateRetransmittablePacket(6));
-  unacked_packets_.AddSentPacket(&packet6, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet6, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  std::vector<QuicPacketNumber> unacked3;
-  std::vector<QuicPacketNumber> retransmittable3;
+  std::vector<uint64_t> unacked3;
+  std::vector<uint64_t> retransmittable3;
   if (unacked_packets_.session_decides_what_to_write()) {
     unacked3 = {3, 5, 6};
     retransmittable3 = {3, 5, 6};
@@ -378,17 +398,17 @@
   }
   VerifyUnackedPackets(&unacked3[0], unacked3.size());
   VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size());
-  QuicPacketNumber pending3[] = {3, 5, 6};
+  uint64_t pending3[] = {3, 5, 6};
   VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3));
 
   // Early retransmit 5 as 7 and ensure in flight packet 3 is not removed.
-  unacked_packets_.IncreaseLargestAcked(6);
-  unacked_packets_.RemoveFromInFlight(6);
-  unacked_packets_.RemoveRetransmittability(6);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(6));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(6));
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(6));
   RetransmitAndSendPacket(5, 7, LOSS_RETRANSMISSION);
 
-  std::vector<QuicPacketNumber> unacked4;
-  std::vector<QuicPacketNumber> retransmittable4;
+  std::vector<uint64_t> unacked4;
+  std::vector<uint64_t> retransmittable4;
   if (unacked_packets_.session_decides_what_to_write()) {
     unacked4 = {3, 5, 7};
     retransmittable4 = {3, 5, 7};
@@ -398,42 +418,44 @@
   }
   VerifyUnackedPackets(&unacked4[0], unacked4.size());
   VerifyRetransmittablePackets(&retransmittable4[0], retransmittable4.size());
-  QuicPacketNumber pending4[] = {3, 5, 7};
+  uint64_t pending4[] = {3, 5, 7};
   VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4));
 
   // Remove the older two transmissions from in flight.
-  unacked_packets_.RemoveFromInFlight(3);
-  unacked_packets_.RemoveFromInFlight(5);
-  QuicPacketNumber pending5[] = {7};
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+  uint64_t pending5[] = {7};
   VerifyInFlightPackets(pending5, QUIC_ARRAYSIZE(pending5));
 }
 
 TEST_P(QuicUnackedPacketMapTest, RetransmitFourTimes) {
   // Simulate a retransmittable packet being sent and retransmitted twice.
   SerializedPacket packet1(CreateRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet1, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   SerializedPacket packet2(CreateRetransmittablePacket(2));
-  unacked_packets_.AddSentPacket(&packet2, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked[] = {1, 2};
+  uint64_t unacked[] = {1, 2};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
   VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
-  QuicPacketNumber retransmittable[] = {1, 2};
+  uint64_t retransmittable[] = {1, 2};
   VerifyRetransmittablePackets(retransmittable,
                                QUIC_ARRAYSIZE(retransmittable));
 
   // Early retransmit 1 as 3.
-  unacked_packets_.IncreaseLargestAcked(2);
-  unacked_packets_.RemoveFromInFlight(2);
-  unacked_packets_.RemoveRetransmittability(2);
-  unacked_packets_.RemoveFromInFlight(1);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
   RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION);
 
-  QuicPacketNumber unacked2[] = {1, 3};
+  uint64_t unacked2[] = {1, 3};
   VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
-  QuicPacketNumber pending2[] = {3};
+  uint64_t pending2[] = {3};
   VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2));
-  std::vector<QuicPacketNumber> retransmittable2;
+  std::vector<uint64_t> retransmittable2;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable2 = {1, 3};
   } else {
@@ -444,13 +466,14 @@
   // TLP 3 (formerly 1) as 4, and don't remove 1 from unacked.
   RetransmitAndSendPacket(3, 4, TLP_RETRANSMISSION);
   SerializedPacket packet5(CreateRetransmittablePacket(5));
-  unacked_packets_.AddSentPacket(&packet5, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet5, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
 
-  QuicPacketNumber unacked3[] = {1, 3, 4, 5};
+  uint64_t unacked3[] = {1, 3, 4, 5};
   VerifyUnackedPackets(unacked3, QUIC_ARRAYSIZE(unacked3));
-  QuicPacketNumber pending3[] = {3, 4, 5};
+  uint64_t pending3[] = {3, 4, 5};
   VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3));
-  std::vector<QuicPacketNumber> retransmittable3;
+  std::vector<uint64_t> retransmittable3;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable3 = {1, 3, 4, 5};
   } else {
@@ -459,23 +482,23 @@
   VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size());
 
   // Early retransmit 4 as 6 and ensure in flight packet 3 is removed.
-  unacked_packets_.IncreaseLargestAcked(5);
-  unacked_packets_.RemoveFromInFlight(5);
-  unacked_packets_.RemoveRetransmittability(5);
-  unacked_packets_.RemoveFromInFlight(3);
-  unacked_packets_.RemoveFromInFlight(4);
+  unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(5));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+  unacked_packets_.RemoveRetransmittability(QuicPacketNumber(5));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
   RetransmitAndSendPacket(4, 6, LOSS_RETRANSMISSION);
 
-  std::vector<QuicPacketNumber> unacked4;
+  std::vector<uint64_t> unacked4;
   if (unacked_packets_.session_decides_what_to_write()) {
     unacked4 = {4, 6};
   } else {
     unacked4 = {4, 6};
   }
   VerifyUnackedPackets(&unacked4[0], unacked4.size());
-  QuicPacketNumber pending4[] = {6};
+  uint64_t pending4[] = {6};
   VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4));
-  std::vector<QuicPacketNumber> retransmittable4;
+  std::vector<uint64_t> retransmittable4;
   if (unacked_packets_.session_decides_what_to_write()) {
     retransmittable4 = {4, 6};
   } else {
@@ -488,18 +511,20 @@
   // Simulate a retransmittable packet being sent, retransmitted, and the first
   // transmission being acked.
   SerializedPacket packet1(CreateRetransmittablePacket(1));
-  unacked_packets_.AddSentPacket(&packet1, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   SerializedPacket packet3(CreateRetransmittablePacket(3));
-  unacked_packets_.AddSentPacket(&packet3, 0, NOT_RETRANSMISSION, now_, true);
+  unacked_packets_.AddSentPacket(&packet3, QuicPacketNumber(),
+                                 NOT_RETRANSMISSION, now_, true);
   RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
 
-  EXPECT_EQ(1u, unacked_packets_.GetLeastUnacked());
-  EXPECT_TRUE(unacked_packets_.IsUnacked(1));
-  EXPECT_FALSE(unacked_packets_.IsUnacked(2));
-  EXPECT_TRUE(unacked_packets_.IsUnacked(3));
-  EXPECT_FALSE(unacked_packets_.IsUnacked(4));
-  EXPECT_TRUE(unacked_packets_.IsUnacked(5));
-  EXPECT_EQ(5u, unacked_packets_.largest_sent_packet());
+  EXPECT_EQ(QuicPacketNumber(1u), unacked_packets_.GetLeastUnacked());
+  EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(1)));
+  EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(2)));
+  EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(3)));
+  EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(4)));
+  EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(5)));
+  EXPECT_EQ(QuicPacketNumber(5u), unacked_packets_.largest_sent_packet());
 }
 
 TEST_P(QuicUnackedPacketMapTest, AggregateContiguousAckedStreamFrames) {
diff --git a/net/third_party/quic/test_tools/quic_connection_peer.cc b/net/third_party/quic/test_tools/quic_connection_peer.cc
index 3b2d1d8..2443523e 100644
--- a/net/third_party/quic/test_tools/quic_connection_peer.cc
+++ b/net/third_party/quic/test_tools/quic_connection_peer.cc
@@ -275,9 +275,8 @@
 }
 
 // static
-bool QuicConnectionPeer::HasRetransmittableFrames(
-    QuicConnection* connection,
-    QuicPacketNumber packet_number) {
+bool QuicConnectionPeer::HasRetransmittableFrames(QuicConnection* connection,
+                                                  uint64_t packet_number) {
   return QuicSentPacketManagerPeer::HasRetransmittableFrames(
       GetSentPacketManager(connection), packet_number);
 }
diff --git a/net/third_party/quic/test_tools/quic_connection_peer.h b/net/third_party/quic/test_tools/quic_connection_peer.h
index 90d63ee..6aede1d 100644
--- a/net/third_party/quic/test_tools/quic_connection_peer.h
+++ b/net/third_party/quic/test_tools/quic_connection_peer.h
@@ -123,7 +123,7 @@
   static void SetAckDecimationDelay(QuicConnection* connection,
                                     float ack_decimation_delay);
   static bool HasRetransmittableFrames(QuicConnection* connection,
-                                       QuicPacketNumber packet_number);
+                                       uint64_t packet_number);
   static bool GetNoStopWaitingFrames(QuicConnection* connection);
   static void SetNoStopWaitingFrames(QuicConnection* connection,
                                      bool no_stop_waiting_frames);
diff --git a/net/third_party/quic/test_tools/quic_packet_creator_peer.cc b/net/third_party/quic/test_tools/quic_packet_creator_peer.cc
index c6a98c9..0d7e6f4 100644
--- a/net/third_party/quic/test_tools/quic_packet_creator_peer.cc
+++ b/net/third_party/quic/test_tools/quic_packet_creator_peer.cc
@@ -44,8 +44,14 @@
 }
 
 void QuicPacketCreatorPeer::SetPacketNumber(QuicPacketCreator* creator,
-                                            QuicPacketNumber s) {
-  creator->packet_.packet_number = s;
+                                            uint64_t s) {
+  DCHECK_NE(0u, s);
+  creator->packet_.packet_number = QuicPacketNumber(s);
+}
+
+// static
+void QuicPacketCreatorPeer::ClearPacketNumber(QuicPacketCreator* creator) {
+  creator->packet_.packet_number.Clear();
 }
 
 // static
diff --git a/net/third_party/quic/test_tools/quic_packet_creator_peer.h b/net/third_party/quic/test_tools/quic_packet_creator_peer.h
index 655e8dd..697995fe1d 100644
--- a/net/third_party/quic/test_tools/quic_packet_creator_peer.h
+++ b/net/third_party/quic/test_tools/quic_packet_creator_peer.h
@@ -27,7 +27,8 @@
       QuicPacketNumberLength packet_number_length);
   static QuicPacketNumberLength GetPacketNumberLength(
       QuicPacketCreator* creator);
-  static void SetPacketNumber(QuicPacketCreator* creator, QuicPacketNumber s);
+  static void SetPacketNumber(QuicPacketCreator* creator, uint64_t s);
+  static void ClearPacketNumber(QuicPacketCreator* creator);
   static void FillPacketHeader(QuicPacketCreator* creator,
                                QuicPacketHeader* header);
   static void CreateStreamFrame(QuicPacketCreator* creator,
diff --git a/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.cc b/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.cc
index 52813e9d..08ffe346 100644
--- a/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -85,18 +85,19 @@
 // static
 bool QuicSentPacketManagerPeer::IsRetransmission(
     QuicSentPacketManager* sent_packet_manager,
-    QuicPacketNumber packet_number) {
+    uint64_t packet_number) {
   DCHECK(HasRetransmittableFrames(sent_packet_manager, packet_number));
   if (!HasRetransmittableFrames(sent_packet_manager, packet_number)) {
     return false;
   }
   if (sent_packet_manager->session_decides_what_to_write()) {
     return sent_packet_manager->unacked_packets_
-               .GetTransmissionInfo(packet_number)
+               .GetTransmissionInfo(QuicPacketNumber(packet_number))
                .transmission_type != NOT_RETRANSMISSION;
   }
   for (auto transmission_info : sent_packet_manager->unacked_packets_) {
-    if (transmission_info.retransmission == packet_number) {
+    if (transmission_info.retransmission.IsInitialized() &&
+        transmission_info.retransmission == QuicPacketNumber(packet_number)) {
       return true;
     }
   }
@@ -106,9 +107,10 @@
 // static
 void QuicSentPacketManagerPeer::MarkForRetransmission(
     QuicSentPacketManager* sent_packet_manager,
-    QuicPacketNumber packet_number,
+    uint64_t packet_number,
     TransmissionType transmission_type) {
-  sent_packet_manager->MarkForRetransmission(packet_number, transmission_type);
+  sent_packet_manager->MarkForRetransmission(QuicPacketNumber(packet_number),
+                                             transmission_type);
 }
 
 // static
@@ -198,16 +200,17 @@
 // static
 bool QuicSentPacketManagerPeer::IsUnacked(
     QuicSentPacketManager* sent_packet_manager,
-    QuicPacketNumber packet_number) {
-  return sent_packet_manager->unacked_packets_.IsUnacked(packet_number);
+    uint64_t packet_number) {
+  return sent_packet_manager->unacked_packets_.IsUnacked(
+      QuicPacketNumber(packet_number));
 }
 
 // static
 bool QuicSentPacketManagerPeer::HasRetransmittableFrames(
     QuicSentPacketManager* sent_packet_manager,
-    QuicPacketNumber packet_number) {
+    uint64_t packet_number) {
   return sent_packet_manager->unacked_packets_.HasRetransmittableFrames(
-      packet_number);
+      QuicPacketNumber(packet_number));
 }
 
 // static
diff --git a/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.h b/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.h
index 4aa02b8..28ded1d 100644
--- a/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/net/third_party/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -52,10 +52,10 @@
 
   // Returns true if |packet_number| is a retransmission of a packet.
   static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
-                               QuicPacketNumber packet_number);
+                               uint64_t packet_number);
 
   static void MarkForRetransmission(QuicSentPacketManager* sent_packet_manager,
-                                    QuicPacketNumber packet_number,
+                                    uint64_t packet_number,
                                     TransmissionType transmission_type);
 
   static QuicTime::Delta GetRetransmissionDelay(
@@ -93,11 +93,11 @@
   static bool UsingPacing(const QuicSentPacketManager* sent_packet_manager);
 
   static bool IsUnacked(QuicSentPacketManager* sent_packet_manager,
-                        QuicPacketNumber packet_number);
+                        uint64_t packet_number);
 
   static bool HasRetransmittableFrames(
       QuicSentPacketManager* sent_packet_manager,
-      QuicPacketNumber packet_number);
+      uint64_t packet_number);
 
   static QuicUnackedPacketMap* GetUnackedPacketMap(
       QuicSentPacketManager* sent_packet_manager);
diff --git a/net/third_party/quic/test_tools/quic_test_utils.cc b/net/third_party/quic/test_tools/quic_test_utils.cc
index 47e3108..237fec7 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.cc
+++ b/net/third_party/quic/test_tools/quic_test_utils.cc
@@ -66,7 +66,7 @@
   DCHECK_GT(ack_blocks.size(), 0u);
 
   QuicAckFrame ack;
-  QuicPacketNumber end_of_previous_block = 1;
+  QuicPacketNumber end_of_previous_block(1);
   for (const QuicAckBlock& block : ack_blocks) {
     DCHECK_GE(block.start, end_of_previous_block);
     DCHECK_GT(block.limit, block.start);
@@ -80,16 +80,21 @@
 }
 
 QuicAckFrame InitAckFrame(uint64_t largest_acked) {
-  return InitAckFrame({{1, largest_acked + 1}});
+  return InitAckFrame(QuicPacketNumber(largest_acked));
+}
+
+QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked) {
+  return InitAckFrame({{QuicPacketNumber(1), largest_acked + 1}});
 }
 
 QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks,
                                        uint64_t least_unacked) {
   QuicAckFrame ack;
-  ack.largest_acked = 2 * num_ack_blocks + least_unacked;
+  ack.largest_acked = QuicPacketNumber(2 * num_ack_blocks + least_unacked);
   // Add enough received packets to get num_ack_blocks ack blocks.
-  for (QuicPacketNumber i = 2; i < 2 * num_ack_blocks + 1; i += 2) {
-    ack.packets.Add(least_unacked + i);
+  for (QuicPacketNumber i = QuicPacketNumber(2);
+       i < QuicPacketNumber(2 * num_ack_blocks + 1); i += 2) {
+    ack.packets.Add(i + least_unacked);
   }
   return ack;
 }
@@ -464,7 +469,7 @@
   // Transfer ownership of the packet to the SentPacketManager and the
   // ack notifier to the AckNotifierManager.
   QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent(
-      packet, 0, QuicTime::Zero(), NOT_RETRANSMISSION,
+      packet, QuicPacketNumber(), QuicTime::Zero(), NOT_RETRANSMISSION,
       HAS_RETRANSMITTABLE_DATA);
 }
 
@@ -847,7 +852,7 @@
   header.version_flag = version_flag;
   header.reset_flag = reset_flag;
   header.packet_number_length = packet_number_length;
-  header.packet_number = packet_number;
+  header.packet_number = QuicPacketNumber(packet_number);
   QuicFrame frame(QuicStreamFrame(
       QuicUtils::GetCryptoStreamId(
           versions != nullptr
@@ -864,8 +869,9 @@
       BuildUnsizedDataPacket(&framer, header, frames));
   EXPECT_TRUE(packet != nullptr);
   char* buffer = new char[kMaxPacketSize];
-  size_t encrypted_length = framer.EncryptPayload(
-      ENCRYPTION_NONE, packet_number, *packet, buffer, kMaxPacketSize);
+  size_t encrypted_length =
+      framer.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(packet_number),
+                            *packet, buffer, kMaxPacketSize);
   EXPECT_NE(0u, encrypted_length);
   return new QuicEncryptedPacket(buffer, encrypted_length, true);
 }
@@ -899,7 +905,7 @@
   header.version_flag = version_flag;
   header.reset_flag = reset_flag;
   header.packet_number_length = packet_number_length;
-  header.packet_number = packet_number;
+  header.packet_number = QuicPacketNumber(packet_number);
   QuicFrame frame(QuicStreamFrame(1, false, 0, QuicStringPiece(data)));
   QuicFrames frames;
   frames.push_back(frame);
@@ -918,8 +924,9 @@
       false /* no diversification nonce */, packet_number_length)] = 0x1F;
 
   char* buffer = new char[kMaxPacketSize];
-  size_t encrypted_length = framer.EncryptPayload(
-      ENCRYPTION_NONE, packet_number, *packet, buffer, kMaxPacketSize);
+  size_t encrypted_length =
+      framer.EncryptPayload(ENCRYPTION_NONE, QuicPacketNumber(packet_number),
+                            *packet, buffer, kMaxPacketSize);
   EXPECT_NE(0u, encrypted_length);
   return new QuicEncryptedPacket(buffer, encrypted_length, true);
 }
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index b9940d60..7841910 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -196,6 +196,7 @@
 // Testing convenience method to construct a QuicAckFrame with 1 ack block which
 // covers packet number range [1, |largest_acked| + 1).
 // Equivalent to InitAckFrame({{1, largest_acked + 1}})
+QuicAckFrame InitAckFrame(uint64_t largest_acked);
 QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked);
 
 // Testing convenience method to construct a QuicAckFrame with |num_ack_blocks|
diff --git a/net/url_request/view_cache_helper.cc b/net/url_request/view_cache_helper.cc
index 0c3df988..2b700a6 100644
--- a/net/url_request/view_cache_helper.cc
+++ b/net/url_request/view_cache_helper.cc
@@ -4,77 +4,15 @@
 
 #include "net/url_request/view_cache_helper.h"
 
+#include <algorithm>
 #include <utility>
 
-#include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/escape.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/request_priority.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/http/http_cache.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "net/url_request/url_request_context.h"
 
-#define VIEW_CACHE_HEAD                                 \
-  "<html><meta charset=\"utf-8\">"                      \
-  "<meta http-equiv=\"Content-Security-Policy\" "       \
-  "  content=\"object-src 'none'; script-src 'none'\">" \
-  "<body><table>"
-
-#define VIEW_CACHE_TAIL \
-  "</table></body></html>"
 
 namespace net {
 
-namespace {
-
-std::string FormatEntryInfo(disk_cache::Entry* entry,
-                            const std::string& url_prefix) {
-  std::string key = entry->GetKey();
-  GURL url = GURL(url_prefix + key);
-  std::string row =
-      "<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) +
-      "</a></td></tr>";
-  return row;
-}
-
-}  // namespace.
-
-ViewCacheHelper::ViewCacheHelper()
-    : context_(NULL),
-      disk_cache_(NULL),
-      entry_(NULL),
-      buf_len_(0),
-      index_(0),
-      data_(NULL),
-      next_state_(STATE_NONE),
-      weak_factory_(this) {
-}
-
-ViewCacheHelper::~ViewCacheHelper() {
-  if (entry_)
-    entry_->Close();
-}
-
-int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
-                                      const URLRequestContext* context,
-                                      std::string* out,
-                                      CompletionOnceCallback callback) {
-  return GetInfoHTML(key, context, std::string(), out, std::move(callback));
-}
-
-int ViewCacheHelper::GetContentsHTML(const URLRequestContext* context,
-                                     const std::string& url_prefix,
-                                     std::string* out,
-                                     CompletionOnceCallback callback) {
-  return GetInfoHTML(std::string(), context, url_prefix, out,
-                     std::move(callback));
-}
-
 // static
 void ViewCacheHelper::HexDump(const char *buf, size_t buf_len,
                               std::string* result) {
@@ -115,257 +53,4 @@
   }
 }
 
-//-----------------------------------------------------------------------------
-
-int ViewCacheHelper::GetInfoHTML(const std::string& key,
-                                 const URLRequestContext* context,
-                                 const std::string& url_prefix,
-                                 std::string* out,
-                                 CompletionOnceCallback callback) {
-  DCHECK(callback_.is_null());
-  DCHECK(context);
-  key_ = key;
-  context_ = context;
-  url_prefix_ = url_prefix;
-  data_ = out;
-  next_state_ = STATE_GET_BACKEND;
-  int rv = DoLoop(OK);
-
-  if (rv == ERR_IO_PENDING)
-    callback_ = std::move(callback);
-
-  return rv;
-}
-
-void ViewCacheHelper::DoCallback(int rv) {
-  DCHECK_NE(ERR_IO_PENDING, rv);
-  DCHECK(!callback_.is_null());
-
-  std::move(callback_).Run(rv);
-}
-
-void ViewCacheHelper::HandleResult(int rv) {
-  DCHECK_NE(ERR_IO_PENDING, rv);
-  DCHECK_NE(ERR_FAILED, rv);
-  context_ = NULL;
-  if (!callback_.is_null())
-    DoCallback(rv);
-}
-
-int ViewCacheHelper::DoLoop(int result) {
-  DCHECK(next_state_ != STATE_NONE);
-
-  int rv = result;
-  do {
-    State state = next_state_;
-    next_state_ = STATE_NONE;
-    switch (state) {
-      case STATE_GET_BACKEND:
-        DCHECK_EQ(OK, rv);
-        rv = DoGetBackend();
-        break;
-      case STATE_GET_BACKEND_COMPLETE:
-        rv = DoGetBackendComplete(rv);
-        break;
-      case STATE_OPEN_NEXT_ENTRY:
-        DCHECK_EQ(OK, rv);
-        rv = DoOpenNextEntry();
-        break;
-      case STATE_OPEN_NEXT_ENTRY_COMPLETE:
-        rv = DoOpenNextEntryComplete(rv);
-        break;
-      case STATE_OPEN_ENTRY:
-        DCHECK_EQ(OK, rv);
-        rv = DoOpenEntry();
-        break;
-      case STATE_OPEN_ENTRY_COMPLETE:
-        rv = DoOpenEntryComplete(rv);
-        break;
-      case STATE_READ_RESPONSE:
-        DCHECK_EQ(OK, rv);
-        rv = DoReadResponse();
-        break;
-      case STATE_READ_RESPONSE_COMPLETE:
-        rv = DoReadResponseComplete(rv);
-        break;
-      case STATE_READ_DATA:
-        DCHECK_EQ(OK, rv);
-        rv = DoReadData();
-        break;
-      case STATE_READ_DATA_COMPLETE:
-        rv = DoReadDataComplete(rv);
-        break;
-
-      default:
-        NOTREACHED() << "bad state";
-        rv = ERR_FAILED;
-        break;
-    }
-  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
-
-  if (rv != ERR_IO_PENDING)
-    HandleResult(rv);
-
-  return rv;
-}
-
-int ViewCacheHelper::DoGetBackend() {
-  next_state_ = STATE_GET_BACKEND_COMPLETE;
-
-  if (!context_->http_transaction_factory())
-    return ERR_FAILED;
-
-  HttpCache* http_cache = context_->http_transaction_factory()->GetCache();
-  if (!http_cache)
-    return ERR_FAILED;
-
-  return http_cache->GetBackend(
-      &disk_cache_,
-      base::BindOnce(&ViewCacheHelper::OnIOComplete, base::Unretained(this)));
-}
-
-int ViewCacheHelper::DoGetBackendComplete(int result) {
-  if (result == ERR_FAILED) {
-    data_->append("no disk cache");
-    return OK;
-  }
-
-  DCHECK_EQ(OK, result);
-  if (key_.empty()) {
-    data_->assign(VIEW_CACHE_HEAD);
-    DCHECK(!iter_);
-    next_state_ = STATE_OPEN_NEXT_ENTRY;
-    return OK;
-  }
-
-  next_state_ = STATE_OPEN_ENTRY;
-  return OK;
-}
-
-int ViewCacheHelper::DoOpenNextEntry() {
-  next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
-  if (!iter_)
-    iter_ = disk_cache_->CreateIterator();
-  return
-      iter_->OpenNextEntry(&entry_, base::Bind(&ViewCacheHelper::OnIOComplete,
-                                               base::Unretained(this)));
-}
-
-int ViewCacheHelper::DoOpenNextEntryComplete(int result) {
-  if (result == ERR_FAILED) {
-    data_->append(VIEW_CACHE_TAIL);
-    return OK;
-  }
-
-  DCHECK_EQ(OK, result);
-  data_->append(FormatEntryInfo(entry_, url_prefix_));
-  entry_->Close();
-  entry_ = NULL;
-
-  next_state_ = STATE_OPEN_NEXT_ENTRY;
-  return OK;
-}
-
-int ViewCacheHelper::DoOpenEntry() {
-  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
-  return disk_cache_->OpenEntry(
-      key_, net::HIGHEST, &entry_,
-      base::Bind(&ViewCacheHelper::OnIOComplete, base::Unretained(this)));
-}
-
-int ViewCacheHelper::DoOpenEntryComplete(int result) {
-  if (result == ERR_FAILED) {
-    data_->append("no matching cache entry for: " + EscapeForHTML(key_));
-    return OK;
-  }
-
-  data_->assign(VIEW_CACHE_HEAD);
-  data_->append(EscapeForHTML(entry_->GetKey()));
-  next_state_ = STATE_READ_RESPONSE;
-  return OK;
-}
-
-int ViewCacheHelper::DoReadResponse() {
-  next_state_ = STATE_READ_RESPONSE_COMPLETE;
-  buf_len_ = entry_->GetDataSize(0);
-  if (!buf_len_)
-    return buf_len_;
-
-  buf_ = base::MakeRefCounted<IOBuffer>(buf_len_);
-  return entry_->ReadData(
-      0,
-      0,
-      buf_.get(),
-      buf_len_,
-      base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr()));
-}
-
-int ViewCacheHelper::DoReadResponseComplete(int result) {
-  if (result && result == buf_len_) {
-    HttpResponseInfo response;
-    bool truncated;
-    if (HttpCache::ParseResponseInfo(
-            buf_->data(), buf_len_, &response, &truncated) &&
-        response.headers.get()) {
-      if (truncated)
-        data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
-
-      data_->append("<hr><pre>");
-      data_->append(EscapeForHTML(response.headers->GetStatusLine()));
-      data_->push_back('\n');
-
-      size_t iter = 0;
-      std::string name, value;
-      while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
-        data_->append(EscapeForHTML(name));
-        data_->append(": ");
-        data_->append(EscapeForHTML(value));
-        data_->push_back('\n');
-      }
-      data_->append("</pre>");
-    }
-  }
-
-  index_ = 0;
-  next_state_ = STATE_READ_DATA;
-  return OK;
-}
-
-int ViewCacheHelper::DoReadData() {
-  data_->append("<hr><pre>");
-
-  next_state_ = STATE_READ_DATA_COMPLETE;
-  buf_len_ = entry_->GetDataSize(index_);
-  if (!buf_len_)
-    return buf_len_;
-
-  buf_ = base::MakeRefCounted<IOBuffer>(buf_len_);
-  return entry_->ReadData(
-      index_,
-      0,
-      buf_.get(),
-      buf_len_,
-      base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr()));
-}
-
-int ViewCacheHelper::DoReadDataComplete(int result) {
-  if (result && result == buf_len_) {
-    HexDump(buf_->data(), buf_len_, data_);
-  }
-  data_->append("</pre>");
-  index_++;
-  if (index_ < HttpCache::kNumCacheEntryDataIndices) {
-    next_state_ = STATE_READ_DATA;
-  } else {
-    data_->append(VIEW_CACHE_TAIL);
-    entry_->Close();
-    entry_ = NULL;
-  }
-  return OK;
-}
-
-void ViewCacheHelper::OnIOComplete(int result) {
-  DoLoop(result);
-}
-
 }  // namespace net.
diff --git a/net/url_request/view_cache_helper.h b/net/url_request/view_cache_helper.h
index 8a76c9c6..897a59d 100644
--- a/net/url_request/view_cache_helper.h
+++ b/net/url_request/view_cache_helper.h
@@ -9,113 +9,16 @@
 
 #include <string>
 
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/io_buffer.h"
 #include "net/base/net_export.h"
-#include "net/disk_cache/disk_cache.h"
 
 namespace net {
 
-class URLRequestContext;
-
 class NET_EXPORT ViewCacheHelper {
  public:
-  ViewCacheHelper();
-  ~ViewCacheHelper();
-
-  // Formats the cache information for |key| as HTML. Returns a net error code.
-  // If this method returns ERR_IO_PENDING, |callback| will be notified when the
-  // operation completes. |out| must remain valid until this operation completes
-  // or the object is destroyed.
-  int GetEntryInfoHTML(const std::string& key,
-                       const URLRequestContext* context,
-                       std::string* out,
-                       CompletionOnceCallback callback);
-
-  // Formats the cache contents as HTML. Returns a net error code.
-  // If this method returns ERR_IO_PENDING, |callback| will be notified when the
-  // operation completes. |out| must remain valid until this operation completes
-  // or the object is destroyed. |url_prefix| will be prepended to each entry
-  // key as a link to the entry.
-  int GetContentsHTML(const URLRequestContext* context,
-                      const std::string& url_prefix,
-                      std::string* out,
-                      CompletionOnceCallback callback);
-
   // Lower-level helper to produce a textual representation of binary data.
   // The results are appended to |result| and can be used in HTML pages
   // provided the dump is contained within <pre></pre> tags.
   static void HexDump(const char *buf, size_t buf_len, std::string* result);
-
- private:
-  enum State {
-    STATE_NONE,
-    STATE_GET_BACKEND,
-    STATE_GET_BACKEND_COMPLETE,
-    STATE_OPEN_NEXT_ENTRY,
-    STATE_OPEN_NEXT_ENTRY_COMPLETE,
-    STATE_OPEN_ENTRY,
-    STATE_OPEN_ENTRY_COMPLETE,
-    STATE_READ_RESPONSE,
-    STATE_READ_RESPONSE_COMPLETE,
-    STATE_READ_DATA,
-    STATE_READ_DATA_COMPLETE
-  };
-
-  // Implements GetEntryInfoHTML and GetContentsHTML.
-  int GetInfoHTML(const std::string& key,
-                  const URLRequestContext* context,
-                  const std::string& url_prefix,
-                  std::string* out,
-                  CompletionOnceCallback callback);
-
-  // This is a helper function used to trigger a completion callback. It may
-  // only be called if callback_ is non-null.
-  void DoCallback(int rv);
-
-  // This will trigger the completion callback if appropriate.
-  void HandleResult(int rv);
-
-  // Runs the state transition loop.
-  int DoLoop(int result);
-
-  // Each of these methods corresponds to a State value. If there is an
-  // argument, the value corresponds to the return of the previous state or
-  // corresponding callback.
-  int DoGetBackend();
-  int DoGetBackendComplete(int result);
-  int DoOpenNextEntry();
-  int DoOpenNextEntryComplete(int result);
-  int DoOpenEntry();
-  int DoOpenEntryComplete(int result);
-  int DoReadResponse();
-  int DoReadResponseComplete(int result);
-  int DoReadData();
-  int DoReadDataComplete(int result);
-
-  // Called to signal completion of asynchronous IO.
-  void OnIOComplete(int result);
-
-  const URLRequestContext* context_;
-  disk_cache::Backend* disk_cache_;
-  disk_cache::Entry* entry_;
-  std::unique_ptr<disk_cache::Backend::Iterator> iter_;
-  scoped_refptr<IOBuffer> buf_;
-  int buf_len_;
-  int index_;
-
-  std::string key_;
-  std::string url_prefix_;
-  std::string* data_;
-  CompletionOnceCallback callback_;
-
-  State next_state_;
-
-  base::WeakPtrFactory<ViewCacheHelper> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ViewCacheHelper);
 };
 
 }  // namespace net.
diff --git a/net/url_request/view_cache_helper_unittest.cc b/net/url_request/view_cache_helper_unittest.cc
index 8694558..8aad4412 100644
--- a/net/url_request/view_cache_helper_unittest.cc
+++ b/net/url_request/view_cache_helper_unittest.cc
@@ -4,226 +4,21 @@
 
 #include "net/url_request/view_cache_helper.h"
 
-#include <memory>
+#include <cstring>
 
-#include "base/pickle.h"
-#include "base/test/scoped_task_environment.h"
-#include "net/base/net_errors.h"
-#include "net/base/request_priority.h"
-#include "net/base/test_completion_callback.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/http/http_cache.h"
-#include "net/http/http_transaction_test_util.h"
-#include "net/test/gtest_util.h"
-#include "net/url_request/url_request_context.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using net::test::IsOk;
-
 namespace net {
 
-namespace {
-
-class TestURLRequestContext : public URLRequestContext {
- public:
-  TestURLRequestContext();
-
-  ~TestURLRequestContext() override { AssertNoURLRequests(); }
-
-  // Gets a pointer to the cache backend.
-  disk_cache::Backend* GetBackend();
-
- private:
-  HttpCache cache_;
-};
-
-TestURLRequestContext::TestURLRequestContext()
-    : cache_(std::make_unique<MockNetworkLayer>(),
-             HttpCache::DefaultBackend::InMemory(0),
-             false /* is_main_cache */) {
-  set_http_transaction_factory(&cache_);
-}
-
-void WriteHeaders(disk_cache::Entry* entry, int flags,
-                  const std::string& data) {
-  if (data.empty())
-    return;
-
-  base::Pickle pickle;
-  pickle.WriteInt(flags | 3);  // Version 3.
-  pickle.WriteInt64(0);
-  pickle.WriteInt64(0);
-  pickle.WriteString(data);
-  pickle.WriteString("example.com");
-  pickle.WriteUInt16(80);
-
-  scoped_refptr<WrappedIOBuffer> buf = base::MakeRefCounted<WrappedIOBuffer>(
-      reinterpret_cast<const char*>(pickle.data()));
-  int len = static_cast<int>(pickle.size());
-
-  TestCompletionCallback cb;
-  int rv = entry->WriteData(0, 0, buf.get(), len, cb.callback(), true);
-  ASSERT_EQ(len, cb.GetResult(rv));
-}
-
-void WriteData(disk_cache::Entry* entry, int index, const std::string& data) {
-  if (data.empty())
-    return;
-
-  int len = data.length();
-  scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(len);
-  memcpy(buf->data(), data.data(), data.length());
-
-  TestCompletionCallback cb;
-  int rv = entry->WriteData(index, 0, buf.get(), len, cb.callback(), true);
-  ASSERT_EQ(len, cb.GetResult(rv));
-}
-
-void WriteToEntry(disk_cache::Backend* cache, const std::string& key,
-                  const std::string& data0, const std::string& data1,
-                  const std::string& data2) {
-  TestCompletionCallback cb;
-  disk_cache::Entry* entry;
-  int rv = cache->CreateEntry(key, net::HIGHEST, &entry, cb.callback());
-  rv = cb.GetResult(rv);
-  if (rv != OK) {
-    rv = cache->OpenEntry(key, net::HIGHEST, &entry, cb.callback());
-    ASSERT_THAT(cb.GetResult(rv), IsOk());
-  }
-
-  WriteHeaders(entry, 0, data0);
-  WriteData(entry, 1, data1);
-  WriteData(entry, 2, data2);
-
-  entry->Close();
-}
-
-void FillCache(URLRequestContext* context) {
-  TestCompletionCallback cb;
-  disk_cache::Backend* cache;
-  int rv =
-      context->http_transaction_factory()->GetCache()->GetBackend(
-          &cache, cb.callback());
-  ASSERT_THAT(cb.GetResult(rv), IsOk());
-
-  std::string empty;
-  WriteToEntry(cache, "first", "some", empty, empty);
-  WriteToEntry(cache, "second", "only hex_dumped", "same", "kind");
-  WriteToEntry(cache, "third", empty, "another", "thing");
-}
-
-}  // namespace.
-
-TEST(ViewCacheHelper, EmptyCache) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
-  TestURLRequestContext context;
-  ViewCacheHelper helper;
-
-  TestCompletionCallback cb;
-  std::string prefix, data;
-  int rv = helper.GetContentsHTML(&context, prefix, &data, cb.callback());
-  EXPECT_THAT(cb.GetResult(rv), IsOk());
-  EXPECT_FALSE(data.empty());
-}
-
-TEST(ViewCacheHelper, ListContents) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
-  TestURLRequestContext context;
-  ViewCacheHelper helper;
-
-  FillCache(&context);
-
-  std::string prefix, data;
-  TestCompletionCallback cb;
-  int rv = helper.GetContentsHTML(&context, prefix, &data, cb.callback());
-  EXPECT_THAT(cb.GetResult(rv), IsOk());
-
-  EXPECT_EQ(0U, data.find("<html>"));
-  EXPECT_NE(std::string::npos, data.find("</html>"));
-  EXPECT_NE(std::string::npos, data.find("first"));
-  EXPECT_NE(std::string::npos, data.find("second"));
-  EXPECT_NE(std::string::npos, data.find("third"));
-
-  EXPECT_EQ(std::string::npos, data.find("some"));
-  EXPECT_EQ(std::string::npos, data.find("same"));
-  EXPECT_EQ(std::string::npos, data.find("thing"));
-}
-
-TEST(ViewCacheHelper, DumpEntry) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
-  TestURLRequestContext context;
-  ViewCacheHelper helper;
-
-  FillCache(&context);
-
-  std::string data;
-  TestCompletionCallback cb;
-  int rv = helper.GetEntryInfoHTML("second", &context, &data, cb.callback());
-  EXPECT_THAT(cb.GetResult(rv), IsOk());
-
-  EXPECT_EQ(0U, data.find("<html>"));
-  EXPECT_NE(std::string::npos, data.find("</html>"));
-
-  EXPECT_NE(std::string::npos, data.find("hex_dumped"));
-  EXPECT_NE(std::string::npos, data.find("same"));
-  EXPECT_NE(std::string::npos, data.find("kind"));
-
-  EXPECT_EQ(std::string::npos, data.find("first"));
-  EXPECT_EQ(std::string::npos, data.find("third"));
-  EXPECT_EQ(std::string::npos, data.find("some"));
-  EXPECT_EQ(std::string::npos, data.find("another"));
-}
-
-// Makes sure the links are correct.
-TEST(ViewCacheHelper, Prefix) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
-  TestURLRequestContext context;
-  ViewCacheHelper helper;
-
-  FillCache(&context);
-
-  std::string key, data;
-  std::string prefix("prefix:");
-  TestCompletionCallback cb;
-  int rv = helper.GetContentsHTML(&context, prefix, &data, cb.callback());
-  EXPECT_THAT(cb.GetResult(rv), IsOk());
-
-  EXPECT_EQ(0U, data.find("<html>"));
-  EXPECT_NE(std::string::npos, data.find("</html>"));
-  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:first\">"));
-  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:second\">"));
-  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:third\">"));
-}
-
-TEST(ViewCacheHelper, TruncatedFlag) {
-  base::test::ScopedTaskEnvironment scoped_task_environment;
-  TestURLRequestContext context;
-  ViewCacheHelper helper;
-
-  TestCompletionCallback cb;
-  disk_cache::Backend* cache;
-  int rv =
-      context.http_transaction_factory()->GetCache()->GetBackend(
-          &cache, cb.callback());
-  ASSERT_THAT(cb.GetResult(rv), IsOk());
-
-  std::string key("the key");
-  disk_cache::Entry* entry;
-  rv = cache->CreateEntry(key, net::HIGHEST, &entry, cb.callback());
-  ASSERT_THAT(cb.GetResult(rv), IsOk());
-
-  // RESPONSE_INFO_TRUNCATED defined on response_info.cc
-  int flags = 1 << 12;
-  WriteHeaders(entry, flags, "something");
-  entry->Close();
-
-  std::string data;
-  TestCompletionCallback cb1;
-  rv = helper.GetEntryInfoHTML(key, &context, &data, cb1.callback());
-  EXPECT_THAT(cb1.GetResult(rv), IsOk());
-
-  EXPECT_NE(std::string::npos, data.find("RESPONSE_INFO_TRUNCATED"));
+TEST(ViewCacheHelper, HexDump) {
+  std::string out = "Prefix\n";
+  const char kIn[] = "0123456789ABCDEFGHIJ\x01\x80<&>";
+  ViewCacheHelper::HexDump(kIn, strlen(kIn), &out);
+  EXPECT_EQ(
+      "Prefix\n00000000: 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46  "
+      "0123456789ABCDEF\n00000010: 47 48 49 4a 01 80 3c 26 3e                  "
+      "     GHIJ..&lt;&amp;&gt;\n",
+      out);
 }
 
 }  // namespace net
diff --git a/services/media_session/audio_focus_manager_unittest.cc b/services/media_session/audio_focus_manager_unittest.cc
index fdc2ff5..ffb4d1f9 100644
--- a/services/media_session/audio_focus_manager_unittest.cc
+++ b/services/media_session/audio_focus_manager_unittest.cc
@@ -249,7 +249,7 @@
   DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerTest);
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     AudioFocusManagerTest,
     testing::Values(mojom::EnforcementMode::kDefault,
diff --git a/services/shape_detection/barcode_detection_impl_mac_unittest.mm b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
index c7cd19f..161510b 100644
--- a/services/shape_detection/barcode_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/barcode_detection_impl_mac_unittest.mm
@@ -168,6 +168,6 @@
   run_loop.Run();
 }
 
-INSTANTIATE_TEST_CASE_P(, BarcodeDetectionImplMacTest, ValuesIn(kTestParams));
+INSTANTIATE_TEST_SUITE_P(, BarcodeDetectionImplMacTest, ValuesIn(kTestParams));
 
 }  // shape_detection namespace
diff --git a/services/shape_detection/face_detection_impl_mac_unittest.mm b/services/shape_detection/face_detection_impl_mac_unittest.mm
index 7353734..450e8b9 100644
--- a/services/shape_detection/face_detection_impl_mac_unittest.mm
+++ b/services/shape_detection/face_detection_impl_mac_unittest.mm
@@ -177,6 +177,6 @@
   run_loop.Run();
 }
 
-INSTANTIATE_TEST_CASE_P(, FaceDetectionImplMacTest, ValuesIn(kTestParams));
+INSTANTIATE_TEST_SUITE_P(, FaceDetectionImplMacTest, ValuesIn(kTestParams));
 
 }  // shape_detection namespace
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index 98cc045..3c95613 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service_executable.gni")
 import("//services/service_manager/public/service_manifest.gni")
 import("//testing/test.gni")
@@ -20,11 +19,6 @@
   ]
 }
 
-service_manifest("manifest") {
-  name = "video_capture"
-  source = "service_manifest.json"
-}
-
 source_set("lib") {
   sources = [
     "broadcasting_receiver.cc",
@@ -102,13 +96,13 @@
 
   deps = [
     ":lib",
-    ":tests_catalog_source",
     ":video_capture",
     "//base/test:test_support",
     "//media/capture:test_support",
     "//media/capture/mojom:video_capture",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp/test:test_support",
+    "//services/video_capture/public/cpp:manifest",
     "//services/video_capture/public/cpp:mocks",
     "//testing/gmock",
     "//testing/gtest",
@@ -119,20 +113,3 @@
     ":video_capture",
   ]
 }
-
-service_manifest("unittest_manifest") {
-  name = "video_capture_unittests"
-  source = "test/service_unittest_manifest.json"
-}
-
-catalog("tests_catalog") {
-  testonly = true
-  embedded_services = [ ":unittest_manifest" ]
-  standalone_services = [ ":manifest" ]
-}
-
-catalog_cpp_source("tests_catalog_source") {
-  testonly = true
-  catalog = ":tests_catalog"
-  generated_function_name = "video_capture::CreateTestCatalog"
-}
diff --git a/services/video_capture/OWNERS b/services/video_capture/OWNERS
index 7607b2d..c3dbbd03 100644
--- a/services/video_capture/OWNERS
+++ b/services/video_capture/OWNERS
@@ -4,6 +4,3 @@
 chfremer@chromium.org
 emircan@chromium.org
 mcasas@chromium.org
-
-per-file service_manifest.json=set noparent
-per-file service_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/services/video_capture/public/cpp/BUILD.gn b/services/video_capture/public/cpp/BUILD.gn
index f83294ad..68f40ca 100644
--- a/services/video_capture/public/cpp/BUILD.gn
+++ b/services/video_capture/public/cpp/BUILD.gn
@@ -25,6 +25,19 @@
   ]
 }
 
+source_set("manifest") {
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+  ]
+  deps = [
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/video_capture/public/mojom",
+    "//services/video_capture/public/mojom:constants",
+  ]
+}
+
 source_set("mocks") {
   testonly = true
 
diff --git a/services/video_capture/public/cpp/OWNERS b/services/video_capture/public/cpp/OWNERS
new file mode 100644
index 0000000..6faeaa47
--- /dev/null
+++ b/services/video_capture/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file manifest.cc=set noparent
+per-file manifest.cc=file://ipc/SECURITY_OWNERS
+per-file manifest.h=set noparent
+per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/services/video_capture/public/cpp/manifest.cc b/services/video_capture/public/cpp/manifest.cc
new file mode 100644
index 0000000..37a1937
--- /dev/null
+++ b/services/video_capture/public/cpp/manifest.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/video_capture/public/cpp/manifest.h"
+
+#include "base/no_destructor.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/testing_controls.mojom.h"
+
+namespace video_capture {
+
+const service_manager::Manifest& GetManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kServiceName)
+          .WithDisplayName("Video Capture")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .WithSandboxType("none")
+                           .WithInstanceSharingPolicy(
+                               service_manager::Manifest::
+                                   InstanceSharingPolicy::kSharedAcrossGroups)
+                           .Build())
+          .ExposeCapability("capture", service_manager::Manifest::InterfaceList<
+                                           mojom::DeviceFactoryProvider>())
+          .ExposeCapability(
+              "tests",
+              service_manager::Manifest::InterfaceList<
+                  mojom::DeviceFactoryProvider, mojom::TestingControls>())
+          .Build()};
+  return *manifest;
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/public/cpp/manifest.h b/services/video_capture/public/cpp/manifest.h
new file mode 100644
index 0000000..7ee0f81
--- /dev/null
+++ b/services/video_capture/public/cpp/manifest.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
+#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace video_capture {
+
+const service_manager::Manifest& GetManifest();
+
+}  // namespace video_capture
+
+#endif  // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
diff --git a/services/video_capture/service_manifest.json b/services/video_capture/service_manifest.json
deleted file mode 100644
index 40c680c..0000000
--- a/services/video_capture/service_manifest.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "name": "video_capture",
-  "display_name": "Video Capture",
-  "sandbox_type": "none",
-  "options" : {
-    "instance_sharing" : "shared_instance_across_users"
-  },
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides" : {
-        "capture" : ["video_capture.mojom.DeviceFactoryProvider"],
-        "tests" : [
-          "video_capture.mojom.DeviceFactoryProvider",
-          "video_capture.mojom.TestingControls"
-        ]
-      }
-    }
-  }
-}
diff --git a/services/video_capture/test/OWNERS b/services/video_capture/test/OWNERS
deleted file mode 100644
index 5415e12..0000000
--- a/services/video_capture/test/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file service_unittest_manifest.json=set noparent
-per-file service_unittest_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/services/video_capture/test/device_factory_provider_test.cc b/services/video_capture/test/device_factory_provider_test.cc
index c7d611e..1e3588fbc 100644
--- a/services/video_capture/test/device_factory_provider_test.cc
+++ b/services/video_capture/test/device_factory_provider_test.cc
@@ -6,14 +6,17 @@
 
 #include "base/command_line.h"
 #include "media/base/media_switches.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
 #include "services/service_manager/public/mojom/constants.mojom.h"
 #include "services/service_manager/public/mojom/service_manager.mojom.h"
+#include "services/video_capture/public/cpp/manifest.h"
 #include "services/video_capture/public/cpp/mock_producer.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/tests_catalog_source.h"
 
 namespace video_capture {
 
+const char kTestServiceName[] = "video_capture_unittests";
+
 DeviceFactoryProviderTest::SharedMemoryVirtualDeviceContext::
     SharedMemoryVirtualDeviceContext(mojom::ProducerRequest producer_request)
     : mock_producer(
@@ -23,10 +26,17 @@
     ~SharedMemoryVirtualDeviceContext() = default;
 
 DeviceFactoryProviderTest::DeviceFactoryProviderTest()
-    : test_service_manager_(CreateTestCatalog()),
-      test_service_binding_(&test_service_,
-                            test_service_manager_.RegisterTestInstance(
-                                "video_capture_unittests")) {}
+    : test_service_manager_(
+          {GetManifest(),
+           service_manager::ManifestBuilder()
+               .WithServiceName(kTestServiceName)
+               .RequireCapability(mojom::kServiceName, "tests")
+               .RequireCapability(service_manager::mojom::kServiceName,
+                                  "service_manager:service_manager")
+               .Build()}),
+      test_service_binding_(
+          &test_service_,
+          test_service_manager_.RegisterTestInstance(kTestServiceName)) {}
 
 DeviceFactoryProviderTest::~DeviceFactoryProviderTest() = default;
 
diff --git a/services/video_capture/test/service_unittest_manifest.json b/services/video_capture/test/service_unittest_manifest.json
deleted file mode 100644
index 4823632..0000000
--- a/services/video_capture/test/service_unittest_manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "video_capture_unittests",
-  "display_name": "Video Capture Unittests",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "video_capture": [ "tests" ],
-        "service_manager": [ "service_manager:service_manager" ]
-      }
-    }
-  }
-}
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc
index 650ad04..c1b9834bc 100644
--- a/sql/sqlite_features_unittest.cc
+++ b/sql/sqlite_features_unittest.cc
@@ -20,11 +20,7 @@
 #include "third_party/sqlite/sqlite3.h"
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
-
 #include "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
 #endif
 
 // Test that certain features are/are-not enabled in our SQLite.
@@ -305,47 +301,30 @@
 }
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-base::ScopedCFTypeRef<CFURLRef> CFURLRefForPath(const base::FilePath& path){
-  base::ScopedCFTypeRef<CFStringRef> urlString(
-      CFStringCreateWithFileSystemRepresentation(
-          kCFAllocatorDefault, path.value().c_str()));
-  base::ScopedCFTypeRef<CFURLRef> url(
-      CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlString,
-                                    kCFURLPOSIXPathStyle, FALSE));
-  return url;
-}
-
 // If a database file is marked to be excluded from Time Machine, verify that
 // journal files are also excluded.
 TEST_F(SQLiteFeaturesTest, TimeMachine) {
   ASSERT_TRUE(db().Execute("CREATE TABLE t (id INTEGER PRIMARY KEY)"));
   db().Close();
 
-  base::FilePath journal = sql::Database::JournalPath(db_path());
+  base::FilePath journal_path = sql::Database::JournalPath(db_path());
   ASSERT_TRUE(GetPathExists(db_path()));
-  ASSERT_TRUE(GetPathExists(journal));
-
-  base::ScopedCFTypeRef<CFURLRef> dbURL(CFURLRefForPath(db_path()));
-  base::ScopedCFTypeRef<CFURLRef> journalURL(CFURLRefForPath(journal));
+  ASSERT_TRUE(GetPathExists(journal_path));
 
   // Not excluded to start.
-  EXPECT_FALSE(CSBackupIsItemExcluded(dbURL, nullptr));
-  EXPECT_FALSE(CSBackupIsItemExcluded(journalURL, nullptr));
+  EXPECT_FALSE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_FALSE(base::mac::GetFileBackupExclusion(journal_path));
 
   // Exclude the main database file.
   EXPECT_TRUE(base::mac::SetFileBackupExclusion(db_path()));
 
-  Boolean excluded_by_path = FALSE;
-  EXPECT_TRUE(CSBackupIsItemExcluded(dbURL, &excluded_by_path));
-  EXPECT_FALSE(excluded_by_path);
-  EXPECT_FALSE(CSBackupIsItemExcluded(journalURL, nullptr));
+  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_FALSE(base::mac::GetFileBackupExclusion(journal_path));
 
   EXPECT_TRUE(db().Open(db_path()));
   ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (1)"));
-  EXPECT_TRUE(CSBackupIsItemExcluded(dbURL, &excluded_by_path));
-  EXPECT_FALSE(excluded_by_path);
-  EXPECT_TRUE(CSBackupIsItemExcluded(journalURL, &excluded_by_path));
-  EXPECT_FALSE(excluded_by_path);
+  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_TRUE(base::mac::GetFileBackupExclusion(journal_path));
 
   // TODO(shess): In WAL mode this will touch -wal and -shm files.  -shm files
   // could be always excluded.
diff --git a/sql/vfs_wrapper.cc b/sql/vfs_wrapper.cc
index 9b7f52d..22e31b1 100644
--- a/sql/vfs_wrapper.cc
+++ b/sql/vfs_wrapper.cc
@@ -9,17 +9,14 @@
 #include <vector>
 
 #include "base/debug/leak_annotations.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_piece.h"
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
-
 #include "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
 #endif
 
 namespace sql {
@@ -164,18 +161,6 @@
   return wrapped_file->pMethods->xUnfetch(wrapped_file, off, p);
 }
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-// Helper to convert a POSIX path into a CoreFoundation path.
-base::ScopedCFTypeRef<CFURLRef> CFURLRefForPath(const char* path){
-  base::ScopedCFTypeRef<CFStringRef> urlString(
-      CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path));
-  base::ScopedCFTypeRef<CFURLRef> url(
-      CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlString,
-                                    kCFURLPOSIXPathStyle, FALSE));
-  return url;
-}
-#endif
-
 int Open(sqlite3_vfs* vfs, const char* file_name, sqlite3_file* wrapper_file,
          int desired_flags, int* used_flags) {
   sqlite3_vfs* wrapped_vfs = GetWrappedVfs(vfs);
@@ -204,14 +189,14 @@
       SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL;
   if (file_name && (desired_flags & kJournalFlags)) {
     // https://www.sqlite.org/c3ref/vfs.html indicates that the journal path
-    // will have a "-suffix".
-    size_t dash_index = base::StringPiece(file_name).rfind('-');
+    // will have a suffix separated by "-" from the main database file name.
+    base::StringPiece file_name_string_piece(file_name);
+    size_t dash_index = file_name_string_piece.rfind('-');
     if (dash_index != base::StringPiece::npos) {
-      std::string db_name(file_name, dash_index);
-      base::ScopedCFTypeRef<CFURLRef> db_url(CFURLRefForPath(db_name.c_str()));
-      if (CSBackupIsItemExcluded(db_url, nullptr)) {
-        base::ScopedCFTypeRef<CFURLRef> journal_url(CFURLRefForPath(file_name));
-        CSBackupSetItemExcluded(journal_url, TRUE, FALSE);
+      base::StringPiece db_name(file_name, dash_index);
+      if (base::mac::GetFileBackupExclusion(base::FilePath(db_name))) {
+        base::mac::SetFileBackupExclusion(
+            base::FilePath(file_name_string_piece));
       }
     }
   }
diff --git a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
index 67d0e4b..487d39654 100644
--- a/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
+++ b/third_party/blink/common/mediastream/media_stream_mojom_traits.cc
@@ -32,6 +32,8 @@
       return blink::mojom::MediaStreamType::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE;
     case blink::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE:
       return blink::mojom::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE;
+    case blink::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE:
+      return blink::mojom::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE;
     case blink::MediaStreamType::NUM_MEDIA_TYPES:
       return blink::mojom::MediaStreamType::NUM_MEDIA_TYPES;
   }
@@ -68,6 +70,9 @@
     case blink::mojom::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE:
       *out = blink::MediaStreamType::MEDIA_DISPLAY_VIDEO_CAPTURE;
       return true;
+    case blink::mojom::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE:
+      *out = blink::MediaStreamType::MEDIA_DISPLAY_AUDIO_CAPTURE;
+      return true;
     case blink::mojom::MediaStreamType::NUM_MEDIA_TYPES:
       *out = blink::MediaStreamType::NUM_MEDIA_TYPES;
       return true;
diff --git a/third_party/blink/common/mediastream/media_stream_request.cc b/third_party/blink/common/mediastream/media_stream_request.cc
index 5583e49..d147103 100644
--- a/third_party/blink/common/mediastream/media_stream_request.cc
+++ b/third_party/blink/common/mediastream/media_stream_request.cc
@@ -12,7 +12,8 @@
 bool IsAudioInputMediaType(MediaStreamType type) {
   return (type == MEDIA_DEVICE_AUDIO_CAPTURE ||
           type == MEDIA_GUM_TAB_AUDIO_CAPTURE ||
-          type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE);
+          type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE ||
+          type == MEDIA_DISPLAY_AUDIO_CAPTURE);
 }
 
 bool IsVideoInputMediaType(MediaStreamType type) {
diff --git a/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h b/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
index 2606de7d..0739524 100644
--- a/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
+++ b/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
@@ -66,7 +66,7 @@
 
   template <typename Interface>
   void GetInterface(mojo::AssociatedInterfacePtr<Interface>* proxy) {
-    GetInterface(mojo::MakeRequest(proxy));
+    GetInterface(mojo::MakeRequest(proxy, task_runner_));
   }
 
   void OverrideBinderForTesting(
diff --git a/third_party/blink/public/common/mediastream/media_stream_request.h b/third_party/blink/public/common/mediastream/media_stream_request.h
index 946c049..8e0e2ba 100644
--- a/third_party/blink/public/common/mediastream/media_stream_request.h
+++ b/third_party/blink/public/common/mediastream/media_stream_request.h
@@ -44,6 +44,7 @@
   // Enables the capture of a user's display, generated by getDisplayMedia()
   // call.
   MEDIA_DISPLAY_VIDEO_CAPTURE,
+  MEDIA_DISPLAY_AUDIO_CAPTURE,
 
   NUM_MEDIA_TYPES
 };
diff --git a/third_party/blink/public/mojom/mediastream/media_stream.mojom b/third_party/blink/public/mojom/mediastream/media_stream.mojom
index 18ef943..24a6bed4 100644
--- a/third_party/blink/public/mojom/mediastream/media_stream.mojom
+++ b/third_party/blink/public/mojom/mediastream/media_stream.mojom
@@ -18,6 +18,7 @@
   MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
   MEDIA_GUM_DESKTOP_AUDIO_CAPTURE,
   MEDIA_DISPLAY_VIDEO_CAPTURE,
+  MEDIA_DISPLAY_AUDIO_CAPTURE,
   NUM_MEDIA_TYPES
 };
 
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index 15aed73..30596aaf 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -79,9 +79,6 @@
   // Returns the http method of the request corresponding to this load.
   virtual WebString HttpMethod() const = 0;
 
-  // Returns the cache mode of the request corresponding to this load.
-  virtual mojom::FetchCacheMode GetCacheMode() const = 0;
-
   // Returns the http referrer of the request corresponding to this load.
   virtual WebString Referrer() const = 0;
 
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index ff81092..94a9f07c 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -169,9 +169,6 @@
   WebURL url;
   // The http method (if any) used to load the main resource.
   WebString http_method;
-  // The cache mode used to load the main resource.
-  // TODO(dgozman): remove this, we are not really using it.
-  mojom::FetchCacheMode cache_mode = mojom::FetchCacheMode::kDefault;
   // The referrer string and policy used to load the main resource.
   WebString referrer;
   network::mojom::ReferrerPolicy referrer_policy =
diff --git a/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc b/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
index 9b01cd0..a2eec822 100644
--- a/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
+++ b/third_party/blink/renderer/core/clipboard/clipboard_utilities.cc
@@ -51,8 +51,7 @@
   uri_list.Split('\n', items);
   // Process the input and return the first valid URL. In case no URLs can
   // be found, return an empty string. This is in line with the HTML5 spec.
-  for (wtf_size_t i = 0; i < items.size(); ++i) {
-    String& line = items[i];
+  for (String& line : items) {
     line = line.StripWhiteSpace();
     if (line.IsEmpty())
       continue;
diff --git a/third_party/blink/renderer/core/clipboard/data_object.cc b/third_party/blink/renderer/core/clipboard/data_object.cc
index 7ee02a7..5e3fefb 100644
--- a/third_party/blink/renderer/core/clipboard/data_object.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object.cc
@@ -165,10 +165,9 @@
 }
 
 String DataObject::GetData(const String& type) const {
-  for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
-    if (item_list_[i]->Kind() == DataObjectItem::kStringKind &&
-        item_list_[i]->GetType() == type)
-      return item_list_[i]->GetAsString();
+  for (const auto& item : item_list_) {
+    if (item->Kind() == DataObjectItem::kStringKind && item->GetType() == type)
+      return item->GetAsString();
   }
   return String();
 }
@@ -207,8 +206,8 @@
 }
 
 bool DataObject::ContainsFilenames() const {
-  for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
-    if (item_list_[i]->IsFilename())
+  for (const auto& item : item_list_) {
+    if (item->IsFilename())
       return true;
   }
   return false;
@@ -216,9 +215,9 @@
 
 Vector<String> DataObject::Filenames() const {
   Vector<String> results;
-  for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
-    if (item_list_[i]->IsFilename())
-      results.push_back(item_list_[i]->GetAsFile()->GetPath());
+  for (const auto& item : item_list_) {
+    if (item->IsFilename())
+      results.push_back(item->GetAsFile()->GetPath());
   }
   return results;
 }
@@ -241,30 +240,29 @@
 DataObject::DataObject() : modifiers_(0) {}
 
 DataObjectItem* DataObject::FindStringItem(const String& type) const {
-  for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
-    if (item_list_[i]->Kind() == DataObjectItem::kStringKind &&
-        item_list_[i]->GetType() == type)
-      return item_list_[i];
+  for (const auto& item : item_list_) {
+    if (item->Kind() == DataObjectItem::kStringKind && item->GetType() == type)
+      return item;
   }
   return nullptr;
 }
 
-bool DataObject::InternalAddStringItem(DataObjectItem* item) {
-  DCHECK_EQ(item->Kind(), DataObjectItem::kStringKind);
-  for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
-    if (item_list_[i]->Kind() == DataObjectItem::kStringKind &&
-        item_list_[i]->GetType() == item->GetType())
+bool DataObject::InternalAddStringItem(DataObjectItem* new_item) {
+  DCHECK_EQ(new_item->Kind(), DataObjectItem::kStringKind);
+  for (const auto& item : item_list_) {
+    if (item->Kind() == DataObjectItem::kStringKind &&
+        item->GetType() == new_item->GetType())
       return false;
   }
 
-  item_list_.push_back(item);
+  item_list_.push_back(new_item);
   NotifyItemListChanged();
   return true;
 }
 
-void DataObject::InternalAddFileItem(DataObjectItem* item) {
-  DCHECK_EQ(item->Kind(), DataObjectItem::kFileKind);
-  item_list_.push_back(item);
+void DataObject::InternalAddFileItem(DataObjectItem* new_item) {
+  DCHECK_EQ(new_item->Kind(), DataObjectItem::kFileKind);
+  item_list_.push_back(new_item);
   NotifyItemListChanged();
 }
 
@@ -274,7 +272,7 @@
 }
 
 void DataObject::NotifyItemListChanged() const {
-  for (const auto& observer : observers_)
+  for (const Member<Observer>& observer : observers_)
     observer->OnItemListChanged();
 }
 
@@ -288,10 +286,7 @@
   DataObject* data_object = Create();
   bool has_file_system = false;
 
-  WebVector<WebDragData::Item> items = data.Items();
-  for (unsigned i = 0; i < items.size(); ++i) {
-    WebDragData::Item item = items[i];
-
+  for (const WebDragData::Item& item : data.Items()) {
     switch (item.storage_type) {
       case WebDragData::Item::kStorageTypeString:
         if (String(item.string_type) == kMimeTypeTextURIList)
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
index 6ec5c3b..c39a22e3 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
@@ -69,10 +69,6 @@
   return DocumentLoader::HttpMethod();
 }
 
-mojom::FetchCacheMode WebDocumentLoaderImpl::GetCacheMode() const {
-  return DocumentLoader::CacheMode();
-}
-
 WebString WebDocumentLoaderImpl::Referrer() const {
   return DocumentLoader::GetReferrer().referrer;
 }
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
index 5a382e7..c617b5f 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
@@ -62,7 +62,6 @@
   WebString OriginalReferrer() const override;
   WebURL GetUrl() const override;
   WebString HttpMethod() const override;
-  mojom::FetchCacheMode GetCacheMode() const override;
   WebString Referrer() const override;
   network::mojom::ReferrerPolicy GetReferrerPolicy() const override;
   const WebURLResponse& GetResponse() const override;
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index f8f832ac..3fbb691 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -7385,10 +7385,30 @@
   EXPECT_EQ(1, web_frame_client.BeginNavigationCallCount());
 }
 
+class TestBeginNavigationCacheModeClient
+    : public frame_test_helpers::TestWebFrameClient {
+ public:
+  TestBeginNavigationCacheModeClient()
+      : cache_mode_(mojom::FetchCacheMode::kDefault) {}
+  ~TestBeginNavigationCacheModeClient() override = default;
+
+  mojom::FetchCacheMode GetCacheMode() const { return cache_mode_; }
+
+  void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
+    cache_mode_ = info->url_request.GetCacheMode();
+    TestWebFrameClient::BeginNavigation(std::move(info));
+  }
+
+ private:
+  mojom::FetchCacheMode cache_mode_;
+};
+
 TEST_F(WebFrameTest, BackToReload) {
   RegisterMockedHttpURLLoad("fragment_middle_click.html");
+  TestBeginNavigationCacheModeClient client;
   frame_test_helpers::WebViewHelper web_view_helper;
-  web_view_helper.InitializeAndLoad(base_url_ + "fragment_middle_click.html");
+  web_view_helper.InitializeAndLoad(base_url_ + "fragment_middle_click.html",
+                                    &client);
   WebLocalFrame* frame = web_view_helper.LocalMainFrame();
   const FrameLoader& main_frame_loader =
       web_view_helper.LocalMainFrame()->GetFrame()->Loader();
@@ -7407,14 +7427,14 @@
             main_frame_loader.GetDocumentLoader()->GetHistoryItem());
 
   frame_test_helpers::ReloadFrame(frame);
-  EXPECT_EQ(mojom::FetchCacheMode::kValidateCache,
-            frame->GetDocumentLoader()->GetCacheMode());
+  EXPECT_EQ(mojom::FetchCacheMode::kValidateCache, client.GetCacheMode());
 }
 
 TEST_F(WebFrameTest, ReloadPost) {
   RegisterMockedHttpURLLoad("reload_post.html");
+  TestBeginNavigationCacheModeClient client;
   frame_test_helpers::WebViewHelper web_view_helper;
-  web_view_helper.InitializeAndLoad(base_url_ + "reload_post.html");
+  web_view_helper.InitializeAndLoad(base_url_ + "reload_post.html", &client);
   WebLocalFrame* frame = web_view_helper.LocalMainFrame();
 
   frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
@@ -7427,8 +7447,7 @@
             frame->GetDocumentLoader()->HttpMethod());
 
   frame_test_helpers::ReloadFrame(frame);
-  EXPECT_EQ(mojom::FetchCacheMode::kValidateCache,
-            frame->GetDocumentLoader()->GetCacheMode());
+  EXPECT_EQ(mojom::FetchCacheMode::kValidateCache, client.GetCacheMode());
   EXPECT_EQ(kWebNavigationTypeFormResubmitted,
             frame->GetDocumentLoader()->GetNavigationType());
 }
@@ -8773,12 +8792,12 @@
   // Check that a reload bypassing cache on a frame will result in the cache
   // policy of the request being set to ReloadBypassingCache.
   RegisterMockedHttpURLLoad("foo.html");
+  TestBeginNavigationCacheModeClient client;
   frame_test_helpers::WebViewHelper web_view_helper;
-  web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
+  web_view_helper.InitializeAndLoad(base_url_ + "foo.html", &client);
   WebLocalFrame* frame = web_view_helper.LocalMainFrame();
   frame_test_helpers::ReloadFrameBypassingCache(frame);
-  EXPECT_EQ(mojom::FetchCacheMode::kBypassCache,
-            frame->GetDocumentLoader()->GetCacheMode());
+  EXPECT_EQ(mojom::FetchCacheMode::kBypassCache, client.GetCacheMode());
 }
 
 static void NodeImageTestValidation(const IntSize& reference_bitmap_size,
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc
index 12d7c67..44fde77 100644
--- a/third_party/blink/renderer/core/exported/web_navigation_params.cc
+++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -28,7 +28,6 @@
   auto result = std::make_unique<WebNavigationParams>();
   result->url = info.url_request.Url();
   result->http_method = info.url_request.HttpMethod();
-  result->cache_mode = info.url_request.GetCacheMode();
   result->referrer = info.url_request.HttpHeaderField(http_names::kReferer);
   result->referrer_policy = info.url_request.GetReferrerPolicy();
   result->http_body = info.url_request.HttpBody();
@@ -62,8 +61,6 @@
     const WebURL& unreachable_url) {
   auto result = WebNavigationParams::CreateWithHTMLString(html, base_url);
   result->unreachable_url = unreachable_url;
-  // Locally generated error pages should not be cached.
-  result->cache_mode = blink::mojom::FetchCacheMode::kNoStore;
   static_cast<WebDocumentLoaderImpl*>(failed_document_loader)
       ->FillNavigationParamsForErrorPage(result.get());
   return result;
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index a47872a..2463e4e2 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -143,12 +143,12 @@
   MockWebMediaPlayer* media_player_;
 };
 
-INSTANTIATE_TEST_CASE_P(Audio,
-                        HTMLMediaElementTest,
-                        testing::Values(MediaTestParam::kAudio));
-INSTANTIATE_TEST_CASE_P(Video,
-                        HTMLMediaElementTest,
-                        testing::Values(MediaTestParam::kVideo));
+INSTANTIATE_TEST_SUITE_P(Audio,
+                         HTMLMediaElementTest,
+                         testing::Values(MediaTestParam::kAudio));
+INSTANTIATE_TEST_SUITE_P(Video,
+                         HTMLMediaElementTest,
+                         testing::Values(MediaTestParam::kVideo));
 
 TEST_P(HTMLMediaElementTest, effectiveMediaVolume) {
   struct TestData {
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 10d1a09ba..174d8f8 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -333,7 +333,13 @@
         &chunk->tokens.at(chunk->pending_csp_meta_token_index);
   }
 
-  if (pending_csp_meta_token_ || !GetDocument()->documentElement()) {
+  bool appcache_initialized = GetDocument()->documentElement();
+  if (!appcache_initialized) {
+    appcache_queueing_start_time_ = CurrentTimeTicks();
+  }
+  // Delay sending some requests if meta tag based CSP is present or
+  // if AppCache was not yet initialized.
+  if (pending_csp_meta_token_ || !appcache_initialized) {
     PreloadRequestStream link_rel_preloads;
     for (auto& request : chunk->preloads) {
       // Link rel preloads don't need to wait for AppCache but they
@@ -1214,6 +1220,14 @@
 
 void HTMLDocumentParser::DocumentElementAvailable() {
   TRACE_EVENT0("blink,loader", "HTMLDocumentParser::documentElementAvailable");
+  TimeDelta delta;
+  if (!appcache_queueing_start_time_.is_null()) {
+    delta = CurrentTimeTicks() - appcache_queueing_start_time_;
+  }
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "WebCore.HTMLDocumentParser.PreloadScannerAppCacheDelayTime", delta,
+      base::TimeDelta::FromMicroseconds(1),
+      base::TimeDelta::FromMilliseconds(1000), 50);
   DCHECK(GetDocument()->documentElement());
   FetchQueuedPreloads();
 }
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index 6ee149a..253f588 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -261,6 +261,7 @@
   base::WeakPtr<BackgroundHTMLParser> background_parser_;
   Member<HTMLResourcePreloader> preloader_;
   PreloadRequestStream queued_preloads_;
+  TimeTicks appcache_queueing_start_time_;
 
   // If this is non-null, then there is a meta CSP token somewhere in the
   // speculation buffer. Preloads will be deferred until a token matching this
diff --git a/third_party/blink/renderer/core/html/parser/xss_auditor.cc b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
index 05e8e9f..1f53134 100644
--- a/third_party/blink/renderer/core/html/parser/xss_auditor.cc
+++ b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
@@ -448,7 +448,7 @@
     xss_protection_ = xss_protection_header;
     if (xss_protection_ == kReflectedXSSInvalid ||
         xss_protection_ == kReflectedXSSUnset) {
-      xss_protection_ = kBlockReflectedXSS;
+      xss_protection_ = kFilterReflectedXSS;
     }
 
     if (auditor_delegate)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index a5d30156..13266c45 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -586,10 +586,16 @@
     intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum();
   box_->SetLogicalHeight(logical_height);
   box_->SetIntrinsicContentLogicalHeight(intrinsic_content_logical_height);
+
   // TODO(mstensho): This should always be done by the parent algorithm, since
   // we may have auto margins, which only the parent is able to resolve. Remove
   // the following line when all layout modes do this properly.
-  box_->SetMargin(ComputePhysicalMargins(constraint_space, Style()));
+  if (box_->IsTableCell()) {
+    // Table-cell margins compute to zero.
+    box_->SetMargin(NGPhysicalBoxStrut());
+  } else {
+    box_->SetMargin(ComputePhysicalMargins(constraint_space, Style()));
+  }
 
   LayoutMultiColumnFlowThread* flow_thread = GetFlowThread(*box_);
   if (UNLIKELY(flow_thread)) {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 466951b2..f9d060e 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -158,7 +158,6 @@
   origin_policy_ = params_->origin_policy;
   requestor_origin_ = params_->requestor_origin;
   unreachable_url_ = params_->unreachable_url;
-  cache_mode_ = params_->cache_mode;
   previews_state_ = params_->previews_state;
 
   if (params_->data.IsNull() && url_.IsAboutSrcdocURL()) {
@@ -290,10 +289,6 @@
   return http_method_;
 }
 
-mojom::FetchCacheMode DocumentLoader::CacheMode() const {
-  return cache_mode_;
-}
-
 const Referrer& DocumentLoader::GetReferrer() const {
   return referrer_;
 }
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 8e5dc48..6b84959 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -129,7 +129,6 @@
   const KURL& Url() const;
   const KURL& UrlForHistory() const;
   const AtomicString& HttpMethod() const;
-  mojom::FetchCacheMode CacheMode() const;
   const Referrer& GetReferrer() const;
   const KURL& UnreachableURL() const;
   EncodedFormData* HttpBody() const;
@@ -360,7 +359,6 @@
   WebURLRequest::PreviewsState previews_state_;
   String origin_policy_;
   scoped_refptr<const SecurityOrigin> requestor_origin_;
-  mojom::FetchCacheMode cache_mode_;
   KURL unreachable_url_;
   std::unique_ptr<WebNavigationBodyLoader> body_loader_;
 
diff --git a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
index 53d51db..8848d38a 100644
--- a/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/test_runner/TestRunner.js
@@ -896,6 +896,25 @@
 };
 
 /**
+ * @param {!SDK.Target} targetToRemove
+ * @return {!Promise<!SDK.Target>}
+ */
+TestRunner.waitForTargetRemoved = function(targetToRemove) {
+  return new Promise(fulfill => {
+    const observer = /** @type {!SDK.TargetManager.Observer} */ ({
+      targetRemoved: function(target) {
+        if (target === targetToRemove) {
+          SDK.targetManager.unobserveTargets(observer);
+          fulfill(target);
+        }
+      },
+      targetAdded: function() {},
+    });
+    SDK.targetManager.observeTargets(observer);
+  });
+};
+
+/**
  * @param {!SDK.RuntimeModel} runtimeModel
  * @return {!Promise}
  */
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 3e1fce6..33717e11 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1161,19 +1161,19 @@
       bool was_validation_message_already_created = validation_message_axid_;
       if (was_validation_message_already_created ||
           form_control->IsValidationMessageVisible()) {
-        AXObject* focused_object = this->FocusedObject();
-        DCHECK(focused_object);
-
-        // Return as long as the focused form control isn't overriding with a
-        // different message via aria-errormessage.
-        bool override_native_validation_message =
-            focused_object->GetAOMPropertyOrARIAAttribute(
-                AOMRelationProperty::kErrorMessage);
-        if (!override_native_validation_message) {
-          AXObject* message = GetOrCreateValidationMessageObject();
-          if (message && !was_validation_message_already_created)
-            ChildrenChanged(document_);
-          return message;
+        AXObject* focused_object = FocusedObject();
+        if (focused_object) {
+          // Return as long as the focused form control isn't overriding with a
+          // different message via aria-errormessage.
+          bool override_native_validation_message =
+              focused_object->GetAOMPropertyOrARIAAttribute(
+                  AOMRelationProperty::kErrorMessage);
+          if (!override_native_validation_message) {
+            AXObject* message = GetOrCreateValidationMessageObject();
+            if (message && !was_validation_message_already_created)
+              ChildrenChanged(document_);
+            return message;
+          }
         }
       }
     }
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h
index b9b76fe5..efd99ec 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_packet_transport.h
@@ -41,7 +41,7 @@
     // set. Packet numbers are used to provide metadata to the implementation of
     // the P2PQuicPacketTransport, but this number is not used by the QUIC
     // library itself.
-    quic::QuicPacketNumber packet_number;
+    uint64_t packet_number;
     const char* buffer;
     size_t buf_len;
   };
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index a7622f3..46819d16 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -73,7 +73,7 @@
     }
 
     P2PQuicPacketTransport::QuicPacket packet;
-    packet.packet_number = connection_->packet_generator().packet_number();
+    packet.packet_number = connection_->packet_generator().packet_number().ToUint64();
     packet.buffer = buffer;
     packet.buf_len = buf_len;
     int bytes_written = packet_transport_->WritePacket(packet);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index ad92df1d..c4d1bebf7 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -225,7 +225,7 @@
   // responsibility of the test to disconnect this delegate
   // (set_delegate(nullptr);) before it is destructed.
   FakePacketTransport* peer_packet_transport_ = nullptr;
-  quic::QuicPacketNumber last_packet_num_;
+  uint64_t last_packet_num_;
   quic::MockClock* clock_;
 };
 
diff --git a/third_party/blink/renderer/modules/webaudio/panner_node.cc b/third_party/blink/renderer/modules/webaudio/panner_node.cc
index 3eca10b..940b008 100644
--- a/third_party/blink/renderer/modules/webaudio/panner_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/panner_node.cc
@@ -495,8 +495,13 @@
   float up_projection = source_listener.Dot(up);
 
   FloatPoint3D projected_source = source_listener - up_projection * up;
+  projected_source.Normalize();
 
-  double azimuth = rad2deg(projected_source.AngleBetween(listener_right));
+  // Don't use AngleBetween here.  It produces the wrong value when one of the
+  // vectors has zero length.  We know here that |projected_source| and
+  // |listener_right| are "normalized", so the dot product is good enough.
+  double azimuth =
+      rad2deg(acos(clampTo(projected_source.Dot(listener_right), -1.0f, 1.0f)));
   FixNANs(azimuth);  // avoid illegal values
 
   // Source  in front or behind the listener
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
index e96a3aa..b9f63c1d 100644
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
@@ -216,9 +216,9 @@
     // Test case 7 (Longer test duration): 256 Pull, 128 Push. 2.5 seconds.
     {48000, 2, 8192, 2500, 256, 0, 128, 1}};
 
-INSTANTIATE_TEST_CASE_P(PushPullFIFOSmokeTest,
-                        PushPullFIFOSmokeTest,
-                        testing::ValuesIn(smoke_test_params));
+INSTANTIATE_TEST_SUITE_P(PushPullFIFOSmokeTest,
+                         PushPullFIFOSmokeTest,
+                         testing::ValuesIn(smoke_test_params));
 
 }  // namespace
 
diff --git a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
index 6d44b00..70fc961 100644
--- a/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
+++ b/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
@@ -358,9 +358,9 @@
      // - Output bus samples (index, expectedValue) = (0, 0), (143, 0)
      {0, 0, 0, 4, {{0, 0}, {1023, 0}}, {{0, 0}, {143, 0}}}}};
 
-INSTANTIATE_TEST_CASE_P(PushPullFIFOFeatureTest,
-                        PushPullFIFOFeatureTest,
-                        testing::ValuesIn(g_feature_test_params));
+INSTANTIATE_TEST_SUITE_P(PushPullFIFOFeatureTest,
+                         PushPullFIFOFeatureTest,
+                         testing::ValuesIn(g_feature_test_params));
 
 }  // namespace
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 73eab32..b8d5939 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -80815,6 +80815,18 @@
      {}
     ]
    ],
+   "css/css-values/viewport-unit-011.html": [
+    [
+     "/css/css-values/viewport-unit-011.html",
+     [
+      [
+       "/css/css-values/reference/viewport-unit-011-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-variables/css-vars-custom-property-case-sensitive-001.html": [
     [
      "/css/css-variables/css-vars-custom-property-case-sensitive-001.html",
@@ -148058,6 +148070,11 @@
      {}
     ]
    ],
+   "css/css-values/reference/viewport-unit-011-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-values/support/1x1-green.png": [
     [
      {}
@@ -217552,6 +217569,12 @@
      {}
     ]
    ],
+   "css/css-values/calc-letter-spacing.html": [
+    [
+     "/css/css-values/calc-letter-spacing.html",
+     {}
+    ]
+   ],
    "css/css-values/calc-rounding-001.html": [
     [
      "/css/css-values/calc-rounding-001.html",
@@ -372249,6 +372272,10 @@
    "a88416a2c2da6532084dc77ded1d3d5ac15e120e",
    "reftest"
   ],
+  "css/css-values/calc-letter-spacing.html": [
+   "444785ba14c21faefe56c22de0c23766ddb26c95",
+   "testharness"
+  ],
   "css/css-values/calc-parenthesis-stack.html": [
    "1d9033d7eecd14066ee9e4f9c52bf1a39e6ddd1b",
    "reftest"
@@ -372409,6 +372436,10 @@
    "5e35f6261e266be2981ae79038e464ac354cc8ef",
    "support"
   ],
+  "css/css-values/reference/viewport-unit-011-ref.html": [
+   "e56c6ec8451370e86fb16f76c50acd627bd5be78",
+   "support"
+  ],
   "css/css-values/support/1x1-green.png": [
    "b98ca0ba0a03c580ac339e4a3653539cfa8edc71",
    "support"
@@ -372669,6 +372700,10 @@
    "dba2af8fa07b07d2ab7c6ca9f657b6e170cd9a41",
    "testharness"
   ],
+  "css/css-values/viewport-unit-011.html": [
+   "055f3d1fd2d716a68d857738493815c8772bef06",
+   "reftest"
+  ],
   "css/css-values/viewport-units-css2-001.html": [
    "c51237dd8a07546d31eef6f6f9b3c84b6ac2b65b",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
index 5f79361..0487968 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
@@ -10,28 +10,28 @@
 PASS minmax replaced IFRAME 9
 PASS minmax replaced IFRAME 10
 PASS minmax replaced IFRAME 11
-PASS minmax replaced async IMG 12
-PASS minmax replaced async IMG 13
-PASS minmax replaced async IMG 14
-PASS minmax replaced async IMG 15
-PASS minmax replaced async IMG 16
-PASS minmax replaced async IMG 17
-PASS minmax replaced async IMG 18
-PASS minmax replaced async IMG 19
-PASS minmax replaced async IMG 20
-PASS minmax replaced async IMG 21
-PASS minmax replaced async IMG 22
-PASS minmax replaced async IMG 23
-PASS minmax replaced async IMG 24
-PASS minmax replaced async IMG 25
-PASS minmax replaced async IMG 26
-PASS minmax replaced async IMG 27
-PASS minmax replaced async IMG 28
-PASS minmax replaced async IMG 29
-PASS minmax replaced async IMG 30
-PASS minmax replaced async IMG 31
-PASS minmax replaced async IMG 32
-PASS minmax replaced async IMG 33
+PASS minmax replaced IMG 12
+PASS minmax replaced IMG 13
+PASS minmax replaced IMG 14
+PASS minmax replaced IMG 15
+PASS minmax replaced IMG 16
+PASS minmax replaced IMG 17
+PASS minmax replaced IMG 18
+PASS minmax replaced IMG 19
+PASS minmax replaced IMG 20
+PASS minmax replaced IMG 21
+PASS minmax replaced IMG 22
+PASS minmax replaced IMG svg 23
+PASS minmax replaced IMG svg 24
+PASS minmax replaced IMG svg 25
+PASS minmax replaced IMG svg 26
+PASS minmax replaced IMG svg 27
+PASS minmax replaced IMG svg 28
+PASS minmax replaced IMG svg 29
+PASS minmax replaced IMG svg 30
+PASS minmax replaced IMG svg 31
+PASS minmax replaced IMG svg 32
+PASS minmax replaced IMG svg 33
 PASS minmax replaced IMG 34
 FAIL minmax replaced IMG 35 assert_equals: incorrect offsetWidth expected "400" but got "438"
 PASS minmax replaced IMG 36
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax.html
index 3f500a0..644b147 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-replaced-minmax.html
@@ -285,10 +285,10 @@
     images[i].src = svgSrc;
   }
 
-  let testNameIndex = 1;
   function makeTest(el) {
     return function() {
       if (!el.getAttribute("data-expected-width")) {
+        // This code is used to generate reference data for the tests.
         let text = `data-expected-width="${el.offsetWidth}" data-expected-height="${el.offsetHeight}" data-offset-y="${el.offsetTop}" data-offset-x="${el.offsetLeft}"`;
         el.parentElement.innerText = text;
         return;
@@ -300,8 +300,15 @@
     }
   };
 
+
+  let testNameIndex = 1;
+  function getTestName(el) {
+    let svg = el.classList.contains("svg") ? " svg" : "";
+    return "minmax replaced " + el.nodeName + svg + " " + testNameIndex++;
+  };
+
   function testAfterImageLoads(img, test) {
-    let asyncTest = async_test("minmax replaced async IMG " + testNameIndex++);
+    let asyncTest = async_test(getTestName(img));
     img.addEventListener("load", _ => {
       asyncTest.step(test);
       asyncTest.done();
@@ -315,7 +322,7 @@
     if (testElements[i].nodeName == "IMG" && !testElements[i].complete) {
       testAfterImageLoads(testElements[i], myTest);
     } else {
-      test(myTest, "minmax replaced " + testElements[i].nodeName + " " + testNameIndex++);
+      test(myTest, getTestName(testElements[i]));
     }
   }
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/no-overflow-with-table-cell-margins.html b/third_party/blink/web_tests/external/wpt/css/css-tables/no-overflow-with-table-cell-margins.html
new file mode 100644
index 0000000..9657da9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/no-overflow-with-table-cell-margins.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/">
+<meta name="assert" content="Table cell margins do not contribute to layout overflow.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+html {
+  display: table;
+  width: 100%;
+  height: 100%;
+}
+body {
+  display: table-cell;
+  width: 100%;
+}
+</style>
+<body></body>
+<script>
+  test(() => {
+    const scroller = document.scrollingElement;
+
+    // There shouldn't be any layout overflow on the root scrollable element.
+    assert_equals(scroller.clientHeight, scroller.scrollHeight);
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-letter-spacing.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-letter-spacing.html
new file mode 100644
index 0000000..444785ba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-letter-spacing.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: computed value of 'letter-spacing' when specified with calc() function</title>
+
+  <!--
+
+  Original test is:
+
+https://chromium.googlesource.com/chromium/src/+/c825d655f6aaf73484f9d56e9012793f5b9668cc/third_party/WebKit/LayoutTests/css3/calc/letter-spacing.html
+
+  -->
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css3-values/#calc-computed-value">
+
+  <meta name="flags" content="invalid">
+  <meta content="This test verifies how 6 calc() functions are computed for 'letter-spacing'." name="assert">
+
+  <script src="/resources/testharness.js"></script>
+
+  <script src="/resources/testharnessreport.js"></script>
+
+  <div id="target"></div>
+
+  <script>
+  function startTesting()
+  {
+
+    function verifyComputedStyle(property_name, initial_value, specified_value, expected_value, description)
+    {
+
+    var elemTarget = document.getElementById("target");
+
+    test(function()
+      {
+
+      elemTarget.style.setProperty(property_name, initial_value);
+
+      /*
+      In exactly 5 out of the 6 sub-tests, the initial_value will
+      act as a fallback value because the calc() function in the
+      specified value generates an invalid value. Since we are
+      running 6 consecutive tests on the same element, then
+      it is necessary to 'reset' its property to an initial
+      value.
+      */
+
+      elemTarget.style.setProperty(property_name, specified_value);
+
+      assert_equals(getComputedStyle(elemTarget)[property_name], expected_value, specified_value + ' should compute to ' + expected_value);
+
+      }, description);
+    }
+
+ /* verifyComputedStyle(property_name, initial_value, specified_value, expected_value, description) */
+
+    verifyComputedStyle("letter-spacing", "20px", "calc(1 + 1px)", "20px", "testing letter-spacing: calc(1 + 1px)");
+
+    verifyComputedStyle("letter-spacing", "20px", "calc(1 + 100%)", "20px", "testing letter-spacing: calc(1 + 100%)");
+
+    verifyComputedStyle("letter-spacing", "20px", "calc(100%)", "20px", "testing letter-spacing: calc(100%)");
+
+    verifyComputedStyle("letter-spacing", "20px", "calc(10px) bla", "20px", "testing letter-spacing: calc(10px) bla");
+
+    verifyComputedStyle("letter-spacing", "20px", "calc(bla) 10px", "20px", "testing letter-spacing: calc(bla) 10px");
+
+    verifyComputedStyle("letter-spacing", "initial", "calc(10px)", "10px", "testing letter-spacing: calc(10px)");
+
+  }
+
+  startTesting();
+
+  </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/reference/viewport-unit-011-ref.html b/third_party/blink/web_tests/external/wpt/css/css-values/reference/viewport-unit-011-ref.html
new file mode 100644
index 0000000..e56c6ec8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/reference/viewport-unit-011-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test Reference File</title>
+
+<style>
+body {
+    margin: 0;
+    height: 100vh;
+}
+div {
+    width: 60%;
+    height: 60%;
+    background-color: green;
+}
+</style>
+<div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/viewport-unit-011.html b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-unit-011.html
new file mode 100644
index 0000000..055f3d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/viewport-unit-011.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: vh unit and vw unit (basic)</title>
+
+  <!--
+  Original test is:
+
+  https://chromium.googlesource.com/chromium/src/+/c825d655f6aaf73484f9d56e9012793f5b9668cc/third_party/WebKit/LayoutTests/css3/calc/viewport-unit.html
+  -->
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css3-values/#viewport-relative-lengths">
+  <link rel="match" href="reference/viewport-unit-011-ref.html">
+
+<style>
+body {
+    margin: 0;
+    height: 100vh;
+}
+div {
+    width: calc(50vw + 10%);
+    height: calc(50vh + 10%);
+    background-color: green;
+}
+</style>
+<div>
+</div>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html
index ea2a4d1..60200b24 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html
@@ -131,16 +131,20 @@
 
         // Set listener properties to "random" values so that motion on one of
         // the attributes actually changes things relative to the panner
-        // location.
+        // location.  And the up and forward directions should have a simple
+        // relationship between them.
         listener.positionX.value = -1;
         listener.positionY.value = 1;
         listener.positionZ.value = -1;
         listener.forwardX.value = -1;
         listener.forwardY.value = 1;
         listener.forwardZ.value = -1;
+        // Make the up vector not parallel or perpendicular to the forward and
+        // position vectors so that automations of the up vector produce
+        // noticeable differences.
         listener.upX.value = 1;
         listener.upY.value = 1;
-        listener.upZ.value = 1;
+        listener.upZ.value = 2;
 
         let audioParam = listener[options.param];
         audioParam.automationRate = 'k-rate';
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html
new file mode 100644
index 0000000..d09f2ec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test Panner Azimuth Calculation</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../../resources/audit.js"></script>
+  </head>
+
+  <body>
+    <script>
+      const audit = Audit.createTaskRunner();
+
+      // Fairly arbitrary sample rate
+      const sampleRate = 16000;
+
+      audit.define('Azimuth calculation', (task, should) => {
+        // Two channels for the context so we can see each channel of the
+        // panner node.
+        let context = new OfflineAudioContext(2, sampleRate, sampleRate);
+
+        let src = new ConstantSourceNode(context);
+        let panner = new PannerNode(context);
+
+        src.connect(panner).connect(context.destination);
+
+        // The source is still pointed directly at the listener, but is now
+        // directly above.  The audio should be the same in both the left and
+        // right channels.
+        panner.positionY.value = 1;
+
+        src.start();
+
+        context.startRendering()
+            .then(audioBuffer => {
+              // The left and right channels should contain the same signal.
+              let c0 = audioBuffer.getChannelData(0);
+              let c1 = audioBuffer.getChannelData(1);
+
+              let expected = Math.fround(Math.SQRT1_2);
+
+              should(c0, 'Left channel').beConstantValueOf(expected);
+              should(c1, 'Righteft channel').beConstantValueOf(expected);
+            })
+            .then(() => task.done());
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/fast/frames/xss-auditor-handles-file-urls-expected.txt b/third_party/blink/web_tests/fast/frames/xss-auditor-handles-file-urls-expected.txt
index 87fcd02c..a4723a9 100644
--- a/third_party/blink/web_tests/fast/frames/xss-auditor-handles-file-urls-expected.txt
+++ b/third_party/blink/web_tests/fast/frames/xss-auditor-handles-file-urls-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE ERROR: line 3: The XSS Auditor blocked access to 'html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==%27%3E' because the source code of a script was found within the request. The auditor was enabled as the server did not send an 'X-XSS-Protection' header.
+CONSOLE ERROR: line 3: The XSS Auditor refused to execute a script in 'html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==%27%3E' because its source code was found within the request. The auditor was enabled as the server did not send an 'X-XSS-Protection' header.
 
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
index e69de29..137d15f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/external/wpt/css/css-position/position-absolute-replaced-minmax-expected.txt
@@ -0,0 +1,40 @@
+This is a testharness.js-based test.
+PASS minmax replaced IFRAME 1
+PASS minmax replaced IFRAME 2
+PASS minmax replaced IFRAME 3
+PASS minmax replaced IFRAME 4
+PASS minmax replaced IFRAME 5
+PASS minmax replaced IFRAME 6
+PASS minmax replaced IFRAME 7
+PASS minmax replaced IFRAME 8
+PASS minmax replaced IFRAME 9
+PASS minmax replaced IFRAME 10
+PASS minmax replaced IFRAME 11
+PASS minmax replaced IMG 12
+PASS minmax replaced IMG 13
+PASS minmax replaced IMG 14
+PASS minmax replaced IMG 15
+PASS minmax replaced IMG 16
+PASS minmax replaced IMG 17
+PASS minmax replaced IMG 18
+PASS minmax replaced IMG 19
+PASS minmax replaced IMG 20
+PASS minmax replaced IMG 21
+PASS minmax replaced IMG 22
+PASS minmax replaced IMG svg 23
+PASS minmax replaced IMG svg 24
+PASS minmax replaced IMG svg 25
+PASS minmax replaced IMG svg 26
+PASS minmax replaced IMG svg 27
+PASS minmax replaced IMG svg 28
+PASS minmax replaced IMG svg 29
+PASS minmax replaced IMG svg 30
+PASS minmax replaced IMG svg 31
+PASS minmax replaced IMG svg 32
+PASS minmax replaced IMG svg 33
+PASS minmax replaced IMG 34
+PASS minmax replaced IMG 35
+PASS minmax replaced IMG 36
+PASS minmax replaced IMG 37
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/repeat-fetch-service-worker.js b/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/repeat-fetch-service-worker.js
new file mode 100644
index 0000000..4b0b3b1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/repeat-fetch-service-worker.js
@@ -0,0 +1,3 @@
+this.addEventListener('fetch', fetchEvent => {
+  fetchEvent.respondWith(fetch(fetchEvent.request));
+});
diff --git a/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/sw-return-useragent.php b/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/sw-return-useragent.php
new file mode 100644
index 0000000..2855ad0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/service-workers/resources/sw-return-useragent.php
@@ -0,0 +1,13 @@
+<html>
+  <head>
+    <title>repeat-fetch-service-worker.js with useragent</title>
+    <script>
+function installSW() {
+  navigator.serviceWorker.register('repeat-fetch-service-worker.js');
+}
+    </script>
+  </head>
+  <body onload="installSW()">
+    <p><?php echo $_SERVER['HTTP_USER_AGENT'] ?></p>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent-expected.txt b/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent-expected.txt
new file mode 100644
index 0000000..6d596db
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent-expected.txt
@@ -0,0 +1,11 @@
+Tests that User-Agent override works for requests from Service Workers.
+
+navigated to http://localhost:8000/devtools/service-workers/resources/sw-return-useragent.php
+user-agent: Mozilla/5.0 (Overridden User Agent)
+awaited service worker target created
+navigated to http://127.0.0.1:8000
+
+Stopped worker and awaited target removal
+navigated to http://localhost:8000/devtools/service-workers/resources/sw-return-useragent.php
+user-agent: Mozilla/5.0 (Overridden User Agent)
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent.js b/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent.js
new file mode 100644
index 0000000..c2ae0cc
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/service-workers/sw-navigate-useragent.js
@@ -0,0 +1,52 @@
+(async function() {
+  TestRunner.addResult(
+      `Tests that User-Agent override works for requests from Service Workers.\n`);
+  await TestRunner.loadModule('application_test_runner');
+  await ApplicationTestRunner.resetState();
+  await TestRunner.showPanel('resources');
+
+  // Suppress racy error message: "error: Connection is closed, can't dispatch pending call"
+  // TODO(jarhar): Fix the root cause of this error message here and in workers-on-navigation.js
+  console.error = () => {};
+
+  const testPage =
+      'http://localhost:8000/devtools/service-workers/resources/sw-return-useragent.php';
+  SDK.multitargetNetworkManager.setUserAgentOverride(
+      'Mozilla/5.0 (Overridden User Agent)');
+
+  const targetAdded = TestRunner.waitForTarget(
+      target => target.type() === SDK.Target.Type.ServiceWorker);
+
+  await TestRunner.navigatePromise(testPage);
+  TestRunner.addResult('navigated to ' + testPage);
+  TestRunner.addResult(
+      'user-agent: ' +
+      await TestRunner.evaluateInPagePromise('document.body.innerText'));
+  const target = await targetAdded;
+  const targetRemoved = TestRunner.waitForTargetRemoved(target);
+  TestRunner.addResult('awaited service worker target created');
+
+  const navigateAwayPage = 'http://127.0.0.1:8000';
+  await TestRunner.navigatePromise(navigateAwayPage);
+  TestRunner.addResult('navigated to ' + navigateAwayPage);
+  TestRunner.addResult('');
+
+  const registrations = TestRunner.serviceWorkerManager.registrations();
+  for (const registrationId of registrations.keys()) {
+    const registration = registrations.get(registrationId);
+    for (const serviceWorkerVersion of registration.versions.values()) {
+      const versionId = serviceWorkerVersion.id;
+      TestRunner.serviceWorkerManager.stopWorker(versionId);
+    }
+  }
+  await targetRemoved;
+  TestRunner.addResult('Stopped worker and awaited target removal');
+
+  await TestRunner.navigatePromise(testPage);
+  TestRunner.addResult('navigated to ' + testPage);
+  TestRunner.addResult(
+      'user-agent: ' +
+      await TestRunner.evaluateInPagePromise('document.body.innerText'));
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/security/xssAuditor/malformed-xss-protection-header-1-expected.txt b/third_party/blink/web_tests/http/tests/security/xssAuditor/malformed-xss-protection-header-1-expected.txt
index 932c79a..b6afee4a 100644
--- a/third_party/blink/web_tests/http/tests/security/xssAuditor/malformed-xss-protection-header-1-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/xssAuditor/malformed-xss-protection-header-1-expected.txt
@@ -1,10 +1,10 @@
 CONSOLE ERROR: Error parsing header X-XSS-Protection: 12345678901234567: expected token to be 0 or 1 at character position 0. The default protections will be applied.
-CONSOLE ERROR: line 4: The XSS Auditor blocked access to 'http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?notifyDone=1&malformed-header=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message%20and%20no%20JavaScript%20alert()%20then%20the%20test%20PASSED.%3C/p%3E' because the source code of a script was found within the request. The auditor was enabled as the server did not send an 'X-XSS-Protection' header.
-ALERT: URL mismatch: '[Location object access threw exception]' vs. 'http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?notifyDone=1&malformed-header=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message%20and%20no%20JavaScript%20alert()%20then%20the%20test%20PASSED.%3C/p%3E'
+CONSOLE ERROR: line 4: The XSS Auditor refused to execute a script in 'http://127.0.0.1:8000/security/xssAuditor/resources/echo-intertag.pl?notifyDone=1&malformed-header=1&q=%3Cscript%3Ealert(String.fromCharCode(0x58,0x53,0x53))%3C/script%3E%3Cp%3EIf%20you%20see%20this%20message%20and%20no%20JavaScript%20alert()%20then%20the%20test%20PASSED.%3C/p%3E' because its source code was found within the request. The auditor was enabled as the server did not send an 'X-XSS-Protection' header.
 This tests that a malformed X-XSS-Protection header is not ignored when the length of its value exceeds 16 characters, and that an error is reported.
 
 --------
 Frame: 'frame'
 --------
-Could not load the requested resource.
-Error code: -28 (net::ERR_BLOCKED_BY_XSS_AUDITOR)
+If you see this message and no JavaScript alert() then the test PASSED.
+
+Page rendered here.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
index be1b136..9b33c06e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
index 6b4b59a..2c60d328 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
index a38de1b..d30d19e6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/international/hebrew-vowels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/svg/text/surrogate-pair-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/svg/text/surrogate-pair-queries-expected.png
deleted file mode 100644
index 5528978..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/svg/text/surrogate-pair-queries-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
index 4e9c333..39cf5de 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
index 9fa7679e..5528978 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/svg/text/surrogate-pair-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
index 76378a9..f284f95 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/atsui-spacing-features-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
index 3f94b96..4c83e26 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
index 567b6937..cb21b18 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/international/hebrew-vowels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
index cc78f133..3a5b6ca4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/combining-character-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
index 2f331b4..cfb9c164 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/ligature-queries-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
index c160869..7e27ed5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/svg/text/surrogate-pair-queries-expected.png
Binary files differ
diff --git a/third_party/closure_compiler/externs/pending.js b/third_party/closure_compiler/externs/pending.js
index 7346a89e..f09f3948 100644
--- a/third_party/closure_compiler/externs/pending.js
+++ b/third_party/closure_compiler/externs/pending.js
@@ -126,3 +126,14 @@
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap
  */
 Array.prototype.flatMap = function(callback, opt_this) {};
+
+/**
+ * TODO(katie): Remove this once length is added to the Closure
+ * chrome_extensions.js.
+ * An event from the TTS engine to communicate the status of an utterance.
+ * @constructor
+ */
+function TtsEvent() {}
+
+/** @type {number} */
+TtsEvent.prototype.length;
diff --git a/third_party/webxr_test_pages/README.chromium b/third_party/webxr_test_pages/README.chromium
index 2e009f54..2fe57d3 100644
--- a/third_party/webxr_test_pages/README.chromium
+++ b/third_party/webxr_test_pages/README.chromium
@@ -12,11 +12,29 @@
 A fork of the WebXR samples to be kept in sync with Chrome and used for testing Chrome.
 
 Local Modifications:
-Forked to provide a stable set of test pages in sync with Chrome.
-Stripped out the media/ directory to minimize the size.
+
+- Forked to provide a stable set of test pages in sync with Chrome.
+
+- Stripped out the media/ directory to minimize the size.
+
+- Adapted samples to work without node compilation
+
+- Added source from https://github.com/toji/gl-matrix (MIT licensed)
+  to js/third-party/gl-matrix, including license file and README.chromium.
+
+- Added missing license file and README.chromium for dat.gui
+
+- Removed the version shim, the samples are intended to work specifically
+  with the ToT Chrome version. The AR samples fall back to legacy-inline-ar
+  mode for now.
 
 Instructions:
+
 In order to serve the samples locally, few steps are required:
-1. Copy contents of src/chrome/test/data/xr/webxr_samples/media into src/third_party/webxr_test_pages/webxr-samples/media
-2. (optional) If editing cottontail code, run `npm install` and `npm run build-all` in src/third_party/webxr_test_pages/webxr-samples/js/cottontail
-3. Serve files, for example by running `python -m SimpleHTTPServer <port number>` in src/third_party/webxr_test_pages/webxr-samples
+
+1. Copy contents of src/chrome/test/data/xr/webxr_samples/media into
+   src/third_party/webxr_test_pages/webxr-samples/media
+
+2. Serve files, for example by running
+   `python -m SimpleHTTPServer <port number>` in
+   src/third_party/webxr_test_pages/webxr-samples
diff --git a/third_party/webxr_test_pages/webxr-samples/360-photos.html b/third_party/webxr_test_pages/webxr-samples/360-photos.html
index 54158223..4884a8df 100644
--- a/third_party/webxr_test_pages/webxr-samples/360-photos.html
+++ b/third_party/webxr_test_pages/webxr-samples/360-photos.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -59,22 +52,23 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {WebXRView, Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -93,22 +87,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -120,12 +110,12 @@
         let fallbackHelper = new FallbackHelper(scene, gl);
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -133,14 +123,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -149,7 +139,7 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -160,12 +150,12 @@
         // 'head-model' frame of reference, which suppresses any positional
         // information from the headset in favor of a head and neck model based
         // solely on the device orientation. (As an added bonus this mode may
-        // be more power efficent on some hardware!)
-        session.requestFrameOfReference('head-model').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        // be more power efficient on some hardware!)
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'position-disabled' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -176,7 +166,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -184,10 +174,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -198,10 +188,10 @@
 
         if (pose) {
           let views = [];
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             let renderView = new WebXRView();
             renderView.projectionMatrix = view.projectionMatrix;
-            renderView.viewMatrix = pose.getViewMatrix(view);
+            renderView.viewMatrix = view.viewMatrix;
             renderView.viewport = session.baseLayer.getViewport(view);
 
             // It's important to take into account which eye the view is
@@ -211,7 +201,7 @@
             views.push(renderView);
           }
 
-          scene.updateInputSources(frame, frameOfRef);
+          scene.updateInputSources(frame, refSpace);
 
           scene.drawViewArray(views);
         }
@@ -221,7 +211,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/fallback-rendering.html b/third_party/webxr_test_pages/webxr-samples/fallback-rendering.html
index 7faceea4..2227290 100644
--- a/third_party/webxr_test_pages/webxr-samples/fallback-rendering.html
+++ b/third_party/webxr_test_pages/webxr-samples/fallback-rendering.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -58,22 +51,23 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
+      import {mat4} from './js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -93,24 +87,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            // If navigator.xr is found but it doesn't have any devices, we'll
-            // need to use the fallback rendering path.
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           // If navigator.xr isn't present in the browser then we need to use
           // the fallback rendering path.
@@ -148,12 +136,12 @@
 
       // Since both the XR and fallback paths need to do the same WebGL
       // initialization code, we've moved that code out to it's own function.
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -161,13 +149,13 @@
         scene.setRenderer(renderer);
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -176,15 +164,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -195,7 +183,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -203,10 +191,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         session.requestAnimationFrame(onXRFrame);
 
@@ -216,12 +204,12 @@
           gl.bindFramebuffer(gl.FRAMEBUFFER, session.baseLayer.framebuffer);
           gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             let viewport = session.baseLayer.getViewport(view);
             gl.viewport(viewport.x, viewport.y,
                         viewport.width, viewport.height);
 
-            scene.draw(view.projectionMatrix, pose.getViewMatrix(view));
+            scene.draw(view.projectionMatrix, view.viewMatrix);
           }
         }
 
@@ -251,7 +239,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/framebuffer-scaling.html b/third_party/webxr_test_pages/webxr-samples/framebuffer-scaling.html
index 12fa781..bf51ea48 100644
--- a/third_party/webxr_test_pages/webxr-samples/framebuffer-scaling.html
+++ b/third_party/webxr_test_pages/webxr-samples/framebuffer-scaling.html
@@ -37,13 +37,6 @@
         but is linked by these samples for wider compatibility.-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -69,17 +62,18 @@
     <main style='text-align: center;'>
       <p>Click 'Enter XR' to see content</p> 
     </main>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {CubeSeaNode} from './js/cottontail/src/nodes/cube-sea.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       let scaleSelect = document.getElementById('framebufferScaleSelect');
       let scaleLabel = document.getElementById('framebufferScaleLabel');
@@ -101,7 +95,7 @@
 
       // XR globals.
       let xrButton = null;
-      let xrFrameOfRef = null;
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -117,22 +111,20 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
         }
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx  }).then(onSessionStarted);
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx  }).then(onSessionStarted);
       }
 
       function onSessionStarted(session) {
@@ -142,7 +134,7 @@
 
         if (!gl) {
           gl = createWebGLContext({
-            compatibleXRDevice: session.device
+            xrCompatible: true
           });
 
           // Set up a non-black clear color so that we can see if something renders wrong.
@@ -165,8 +157,8 @@
           framebufferScaleFactor: scale
         });
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
 
           session.requestAnimationFrame(onXRFrame);
         });
@@ -185,13 +177,13 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let pose = frame.getDevicePose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, xrFrameOfRef);
+        scene.updateInputSources(frame, xrRefSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -200,7 +192,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/input-selection.html b/third_party/webxr_test_pages/webxr-samples/input-selection.html
index f9b815f8..b5fd1770 100644
--- a/third_party/webxr_test_pages/webxr-samples/input-selection.html
+++ b/third_party/webxr_test_pages/webxr-samples/input-selection.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -58,25 +51,29 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {Node} from './js/cottontail/src/core/node.js';
+      import {PbrMaterial} from './js/cottontail/src/materials/pbr.js';
+      import {BoxBuilder} from './js/cottontail/src/geometry/box-builder.js';
+      import {mat4} from './js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // Temporary
       let hideStats = QueryArgs.getBool('hideStats', false);
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -98,22 +95,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -126,12 +119,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -165,14 +158,14 @@
         addBox(1.0, 1.6, -1.3, 0.0, 0.0, 1.0);
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -185,15 +178,15 @@
         // performed some sort of primary input action and respond to it.
         session.addEventListener('select', onSelect);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -201,11 +194,11 @@
       }
 
       function onSelect(ev) {
-        let frameOfRef = ev.frame.session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
+        let refSpace = ev.frame.session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
 
-        let inputPose = ev.frame.getInputPose(ev.inputSource, frameOfRef);
+        let inputPose = ev.frame.getInputPose(ev.inputSource, refSpace);
         if (!inputPose) {
           return;
         }
@@ -230,7 +223,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -238,10 +231,10 @@
 
       function onXRFrame(time, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -260,7 +253,7 @@
         // to automatically add the right meshes for the session's input sources
         // each frame. This also does simple hit detection to position the 
         // cursors correctly on the surface of selectable nodes.
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -269,7 +262,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/input-tracking.html b/third_party/webxr_test_pages/webxr-samples/input-tracking.html
index 487289c9..96e3e9f 100644
--- a/third_party/webxr_test_pages/webxr-samples/input-tracking.html
+++ b/third_party/webxr_test_pages/webxr-samples/input-tracking.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -59,22 +52,23 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -92,25 +86,21 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            // Note: If you don't want dragging on the canvas to do things like
-            // scroll or pull-to-refresh, you'll want to set touch-action: none;
-            // on the canvas' CSS style, which this page does in common.css
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          // Note: If you don't want dragging on the canvas to do things like
+          // scroll or pull-to-refresh, you'll want to set touch-action: none;
+          // on the canvas' CSS style, which this page does in common.css
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -123,12 +113,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -139,14 +129,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -155,15 +145,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -175,17 +165,17 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
       }
 
-      function updateInputSources(session, frame, frameOfRef) {
+      function updateInputSources(session, frame, refSpace) {
         let inputSources = session.getInputSources();
 
         for (let inputSource of inputSources) {
-          let inputPose = frame.getInputPose(inputSource, frameOfRef);
+          let inputPose = frame.getInputPose(inputSource, refSpace);
 
           // We may not get a pose back in cases where the input source has lost
           // tracking or does not know where it is relative to the given frame
@@ -234,16 +224,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        updateInputSources(session, frame, frameOfRef);
+        updateInputSources(session, frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -252,7 +242,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js
deleted file mode 100644
index 40deaa60..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js
+++ /dev/null
@@ -1,15198 +0,0 @@
-/*!
- * Copyright 2018 The Immersive Web Community Group
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- * 
- */
-(function webpackUniversalModuleDefinition(root, factory) {
-	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory();
-	else if(typeof define === 'function' && define.amd)
-		define([], factory);
-	else {
-		var a = factory();
-		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
-	}
-})(window, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/ 	// The module cache
-/******/ 	var installedModules = {};
-/******/
-/******/ 	// The require function
-/******/ 	function __webpack_require__(moduleId) {
-/******/
-/******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId]) {
-/******/ 			return installedModules[moduleId].exports;
-/******/ 		}
-/******/ 		// Create a new module (and put it into the cache)
-/******/ 		var module = installedModules[moduleId] = {
-/******/ 			i: moduleId,
-/******/ 			l: false,
-/******/ 			exports: {}
-/******/ 		};
-/******/
-/******/ 		// Execute the module function
-/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-/******/
-/******/ 		// Flag the module as loaded
-/******/ 		module.l = true;
-/******/
-/******/ 		// Return the exports of the module
-/******/ 		return module.exports;
-/******/ 	}
-/******/
-/******/
-/******/ 	// expose the modules object (__webpack_modules__)
-/******/ 	__webpack_require__.m = modules;
-/******/
-/******/ 	// expose the module cache
-/******/ 	__webpack_require__.c = installedModules;
-/******/
-/******/ 	// define getter function for harmony exports
-/******/ 	__webpack_require__.d = function(exports, name, getter) {
-/******/ 		if(!__webpack_require__.o(exports, name)) {
-/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
-/******/ 		}
-/******/ 	};
-/******/
-/******/ 	// define __esModule on exports
-/******/ 	__webpack_require__.r = function(exports) {
-/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
-/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
-/******/ 		}
-/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
-/******/ 	};
-/******/
-/******/ 	// create a fake namespace object
-/******/ 	// mode & 1: value is a module id, require it
-/******/ 	// mode & 2: merge all properties of value into the ns
-/******/ 	// mode & 4: return value when already ns object
-/******/ 	// mode & 8|1: behave like require
-/******/ 	__webpack_require__.t = function(value, mode) {
-/******/ 		if(mode & 1) value = __webpack_require__(value);
-/******/ 		if(mode & 8) return value;
-/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
-/******/ 		var ns = Object.create(null);
-/******/ 		__webpack_require__.r(ns);
-/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
-/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
-/******/ 		return ns;
-/******/ 	};
-/******/
-/******/ 	// getDefaultExport function for compatibility with non-harmony modules
-/******/ 	__webpack_require__.n = function(module) {
-/******/ 		var getter = module && module.__esModule ?
-/******/ 			function getDefault() { return module['default']; } :
-/******/ 			function getModuleExports() { return module; };
-/******/ 		__webpack_require__.d(getter, 'a', getter);
-/******/ 		return getter;
-/******/ 	};
-/******/
-/******/ 	// Object.prototype.hasOwnProperty.call
-/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
-/******/
-/******/ 	// __webpack_public_path__
-/******/ 	__webpack_require__.p = "";
-/******/
-/******/
-/******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = "./src/cottontail.js");
-/******/ })
-/************************************************************************/
-/******/ ({
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/common.js":
-/*!********************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/common.js ***!
-  \********************************************************/
-/*! exports provided: EPSILON, ARRAY_TYPE, RANDOM, setMatrixArrayType, toRadian, equals */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EPSILON", function() { return EPSILON; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ARRAY_TYPE", function() { return ARRAY_TYPE; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RANDOM", function() { return RANDOM; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setMatrixArrayType", function() { return setMatrixArrayType; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toRadian", function() { return toRadian; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/**
- * Common utilities
- * @module glMatrix
- */
-
-// Configuration Constants
-const EPSILON = 0.000001;
-let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array;
-const RANDOM = Math.random;
-
-/**
- * Sets the type of array used when creating new vectors and matrices
- *
- * @param {Type} type Array type, such as Float32Array or Array
- */
-function setMatrixArrayType(type) {
-  ARRAY_TYPE = type;
-}
-
-const degree = Math.PI / 180;
-
-/**
- * Convert Degree To Radian
- *
- * @param {Number} a Angle in Degrees
- */
-function toRadian(a) {
-  return a * degree;
-}
-
-/**
- * Tests whether or not the arguments have approximately the same value, within an absolute
- * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
- * than or equal to 1.0, and a relative tolerance is used for larger values)
- *
- * @param {Number} a The first number to test.
- * @param {Number} b The second number to test.
- * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
- */
-function equals(a, b) {
-  return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b));
-}
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/mat2.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/mat2.js ***!
-  \******************************************************/
-/*! exports provided: create, clone, copy, identity, fromValues, set, transpose, invert, adjoint, determinant, multiply, rotate, scale, fromRotation, fromScaling, str, frob, LDU, add, subtract, exactEquals, equals, multiplyScalar, multiplyScalarAndAdd, mul, sub */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transpose", function() { return transpose; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "adjoint", function() { return adjoint; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "determinant", function() { return determinant; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotation", function() { return fromRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromScaling", function() { return fromScaling; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "frob", function() { return frob; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LDU", function() { return LDU; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalar", function() { return multiplyScalar; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalarAndAdd", function() { return multiplyScalarAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 2x2 Matrix
- * @module mat2
- */
-
-/**
- * Creates a new identity mat2
- *
- * @returns {mat2} a new 2x2 matrix
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[1] = 0;
-    out[2] = 0;
-  }
-  out[0] = 1;
-  out[3] = 1;
-  return out;
-}
-
-/**
- * Creates a new mat2 initialized with values from an existing matrix
- *
- * @param {mat2} a matrix to clone
- * @returns {mat2} a new 2x2 matrix
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Copy the values from one mat2 to another
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the source matrix
- * @returns {mat2} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Set a mat2 to the identity matrix
- *
- * @param {mat2} out the receiving matrix
- * @returns {mat2} out
- */
-function identity(out) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  return out;
-}
-
-/**
- * Create a new mat2 with the given values
- *
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m10 Component in column 1, row 0 position (index 2)
- * @param {Number} m11 Component in column 1, row 1 position (index 3)
- * @returns {mat2} out A new 2x2 matrix
- */
-function fromValues(m00, m01, m10, m11) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m10;
-  out[3] = m11;
-  return out;
-}
-
-/**
- * Set the components of a mat2 to the given values
- *
- * @param {mat2} out the receiving matrix
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m10 Component in column 1, row 0 position (index 2)
- * @param {Number} m11 Component in column 1, row 1 position (index 3)
- * @returns {mat2} out
- */
-function set(out, m00, m01, m10, m11) {
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m10;
-  out[3] = m11;
-  return out;
-}
-
-/**
- * Transpose the values of a mat2
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the source matrix
- * @returns {mat2} out
- */
-function transpose(out, a) {
-  // If we are transposing ourselves we can skip a few steps but have to cache
-  // some values
-  if (out === a) {
-    let a1 = a[1];
-    out[1] = a[2];
-    out[2] = a1;
-  } else {
-    out[0] = a[0];
-    out[1] = a[2];
-    out[2] = a[1];
-    out[3] = a[3];
-  }
-
-  return out;
-}
-
-/**
- * Inverts a mat2
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the source matrix
- * @returns {mat2} out
- */
-function invert(out, a) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-
-  // Calculate the determinant
-  let det = a0 * a3 - a2 * a1;
-
-  if (!det) {
-    return null;
-  }
-  det = 1.0 / det;
-
-  out[0] =  a3 * det;
-  out[1] = -a1 * det;
-  out[2] = -a2 * det;
-  out[3] =  a0 * det;
-
-  return out;
-}
-
-/**
- * Calculates the adjugate of a mat2
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the source matrix
- * @returns {mat2} out
- */
-function adjoint(out, a) {
-  // Caching this value is nessecary if out == a
-  let a0 = a[0];
-  out[0] =  a[3];
-  out[1] = -a[1];
-  out[2] = -a[2];
-  out[3] =  a0;
-
-  return out;
-}
-
-/**
- * Calculates the determinant of a mat2
- *
- * @param {mat2} a the source matrix
- * @returns {Number} determinant of a
- */
-function determinant(a) {
-  return a[0] * a[3] - a[2] * a[1];
-}
-
-/**
- * Multiplies two mat2's
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the first operand
- * @param {mat2} b the second operand
- * @returns {mat2} out
- */
-function multiply(out, a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
-  out[0] = a0 * b0 + a2 * b1;
-  out[1] = a1 * b0 + a3 * b1;
-  out[2] = a0 * b2 + a2 * b3;
-  out[3] = a1 * b2 + a3 * b3;
-  return out;
-}
-
-/**
- * Rotates a mat2 by the given angle
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat2} out
- */
-function rotate(out, a, rad) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  out[0] = a0 *  c + a2 * s;
-  out[1] = a1 *  c + a3 * s;
-  out[2] = a0 * -s + a2 * c;
-  out[3] = a1 * -s + a3 * c;
-  return out;
-}
-
-/**
- * Scales the mat2 by the dimensions in the given vec2
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the matrix to rotate
- * @param {vec2} v the vec2 to scale the matrix by
- * @returns {mat2} out
- **/
-function scale(out, a, v) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let v0 = v[0], v1 = v[1];
-  out[0] = a0 * v0;
-  out[1] = a1 * v0;
-  out[2] = a2 * v1;
-  out[3] = a3 * v1;
-  return out;
-}
-
-/**
- * Creates a matrix from a given angle
- * This is equivalent to (but much faster than):
- *
- *     mat2.identity(dest);
- *     mat2.rotate(dest, dest, rad);
- *
- * @param {mat2} out mat2 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat2} out
- */
-function fromRotation(out, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  out[0] = c;
-  out[1] = s;
-  out[2] = -s;
-  out[3] = c;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector scaling
- * This is equivalent to (but much faster than):
- *
- *     mat2.identity(dest);
- *     mat2.scale(dest, dest, vec);
- *
- * @param {mat2} out mat2 receiving operation result
- * @param {vec2} v Scaling vector
- * @returns {mat2} out
- */
-function fromScaling(out, v) {
-  out[0] = v[0];
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = v[1];
-  return out;
-}
-
-/**
- * Returns a string representation of a mat2
- *
- * @param {mat2} a matrix to represent as a string
- * @returns {String} string representation of the matrix
- */
-function str(a) {
-  return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
-}
-
-/**
- * Returns Frobenius norm of a mat2
- *
- * @param {mat2} a the matrix to calculate Frobenius norm of
- * @returns {Number} Frobenius norm
- */
-function frob(a) {
-  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2)))
-}
-
-/**
- * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
- * @param {mat2} L the lower triangular matrix
- * @param {mat2} D the diagonal matrix
- * @param {mat2} U the upper triangular matrix
- * @param {mat2} a the input matrix to factorize
- */
-
-function LDU(L, D, U, a) {
-  L[2] = a[2]/a[0];
-  U[0] = a[0];
-  U[1] = a[1];
-  U[3] = a[3] - L[2] * U[1];
-  return [L, D, U];
-}
-
-/**
- * Adds two mat2's
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the first operand
- * @param {mat2} b the second operand
- * @returns {mat2} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  return out;
-}
-
-/**
- * Subtracts matrix b from matrix a
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the first operand
- * @param {mat2} b the second operand
- * @returns {mat2} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  out[3] = a[3] - b[3];
-  return out;
-}
-
-/**
- * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
- *
- * @param {mat2} a The first matrix.
- * @param {mat2} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
-}
-
-/**
- * Returns whether or not the matrices have approximately the same elements in the same position.
- *
- * @param {mat2} a The first matrix.
- * @param {mat2} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-          Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
-}
-
-/**
- * Multiply each element of the matrix by a scalar.
- *
- * @param {mat2} out the receiving matrix
- * @param {mat2} a the matrix to scale
- * @param {Number} b amount to scale the matrix's elements by
- * @returns {mat2} out
- */
-function multiplyScalar(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  return out;
-}
-
-/**
- * Adds two mat2's after multiplying each element of the second operand by a scalar value.
- *
- * @param {mat2} out the receiving vector
- * @param {mat2} a the first operand
- * @param {mat2} b the second operand
- * @param {Number} scale the amount to scale b's elements by before adding
- * @returns {mat2} out
- */
-function multiplyScalarAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  out[3] = a[3] + (b[3] * scale);
-  return out;
-}
-
-/**
- * Alias for {@link mat2.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link mat2.subtract}
- * @function
- */
-const sub = subtract;
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/mat2d.js":
-/*!*******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/mat2d.js ***!
-  \*******************************************************/
-/*! exports provided: create, clone, copy, identity, fromValues, set, invert, determinant, multiply, rotate, scale, translate, fromRotation, fromScaling, fromTranslation, str, frob, add, subtract, multiplyScalar, multiplyScalarAndAdd, exactEquals, equals, mul, sub */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "determinant", function() { return determinant; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "translate", function() { return translate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotation", function() { return fromRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromScaling", function() { return fromScaling; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromTranslation", function() { return fromTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "frob", function() { return frob; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalar", function() { return multiplyScalar; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalarAndAdd", function() { return multiplyScalarAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 2x3 Matrix
- * @module mat2d
- *
- * @description
- * A mat2d contains six elements defined as:
- * <pre>
- * [a, c, tx,
- *  b, d, ty]
- * </pre>
- * This is a short form for the 3x3 matrix:
- * <pre>
- * [a, c, tx,
- *  b, d, ty,
- *  0, 0, 1]
- * </pre>
- * The last row is ignored so the array is shorter and operations are faster.
- */
-
-/**
- * Creates a new identity mat2d
- *
- * @returns {mat2d} a new 2x3 matrix
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](6);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[1] = 0;
-    out[2] = 0;
-    out[4] = 0;
-    out[5] = 0;
-  }
-  out[0] = 1;
-  out[3] = 1;
-  return out;
-}
-
-/**
- * Creates a new mat2d initialized with values from an existing matrix
- *
- * @param {mat2d} a matrix to clone
- * @returns {mat2d} a new 2x3 matrix
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](6);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  return out;
-}
-
-/**
- * Copy the values from one mat2d to another
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the source matrix
- * @returns {mat2d} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  return out;
-}
-
-/**
- * Set a mat2d to the identity matrix
- *
- * @param {mat2d} out the receiving matrix
- * @returns {mat2d} out
- */
-function identity(out) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  out[4] = 0;
-  out[5] = 0;
-  return out;
-}
-
-/**
- * Create a new mat2d with the given values
- *
- * @param {Number} a Component A (index 0)
- * @param {Number} b Component B (index 1)
- * @param {Number} c Component C (index 2)
- * @param {Number} d Component D (index 3)
- * @param {Number} tx Component TX (index 4)
- * @param {Number} ty Component TY (index 5)
- * @returns {mat2d} A new mat2d
- */
-function fromValues(a, b, c, d, tx, ty) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](6);
-  out[0] = a;
-  out[1] = b;
-  out[2] = c;
-  out[3] = d;
-  out[4] = tx;
-  out[5] = ty;
-  return out;
-}
-
-/**
- * Set the components of a mat2d to the given values
- *
- * @param {mat2d} out the receiving matrix
- * @param {Number} a Component A (index 0)
- * @param {Number} b Component B (index 1)
- * @param {Number} c Component C (index 2)
- * @param {Number} d Component D (index 3)
- * @param {Number} tx Component TX (index 4)
- * @param {Number} ty Component TY (index 5)
- * @returns {mat2d} out
- */
-function set(out, a, b, c, d, tx, ty) {
-  out[0] = a;
-  out[1] = b;
-  out[2] = c;
-  out[3] = d;
-  out[4] = tx;
-  out[5] = ty;
-  return out;
-}
-
-/**
- * Inverts a mat2d
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the source matrix
- * @returns {mat2d} out
- */
-function invert(out, a) {
-  let aa = a[0], ab = a[1], ac = a[2], ad = a[3];
-  let atx = a[4], aty = a[5];
-
-  let det = aa * ad - ab * ac;
-  if(!det){
-    return null;
-  }
-  det = 1.0 / det;
-
-  out[0] = ad * det;
-  out[1] = -ab * det;
-  out[2] = -ac * det;
-  out[3] = aa * det;
-  out[4] = (ac * aty - ad * atx) * det;
-  out[5] = (ab * atx - aa * aty) * det;
-  return out;
-}
-
-/**
- * Calculates the determinant of a mat2d
- *
- * @param {mat2d} a the source matrix
- * @returns {Number} determinant of a
- */
-function determinant(a) {
-  return a[0] * a[3] - a[1] * a[2];
-}
-
-/**
- * Multiplies two mat2d's
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the first operand
- * @param {mat2d} b the second operand
- * @returns {mat2d} out
- */
-function multiply(out, a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
-  out[0] = a0 * b0 + a2 * b1;
-  out[1] = a1 * b0 + a3 * b1;
-  out[2] = a0 * b2 + a2 * b3;
-  out[3] = a1 * b2 + a3 * b3;
-  out[4] = a0 * b4 + a2 * b5 + a4;
-  out[5] = a1 * b4 + a3 * b5 + a5;
-  return out;
-}
-
-/**
- * Rotates a mat2d by the given angle
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat2d} out
- */
-function rotate(out, a, rad) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  out[0] = a0 *  c + a2 * s;
-  out[1] = a1 *  c + a3 * s;
-  out[2] = a0 * -s + a2 * c;
-  out[3] = a1 * -s + a3 * c;
-  out[4] = a4;
-  out[5] = a5;
-  return out;
-}
-
-/**
- * Scales the mat2d by the dimensions in the given vec2
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the matrix to translate
- * @param {vec2} v the vec2 to scale the matrix by
- * @returns {mat2d} out
- **/
-function scale(out, a, v) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
-  let v0 = v[0], v1 = v[1];
-  out[0] = a0 * v0;
-  out[1] = a1 * v0;
-  out[2] = a2 * v1;
-  out[3] = a3 * v1;
-  out[4] = a4;
-  out[5] = a5;
-  return out;
-}
-
-/**
- * Translates the mat2d by the dimensions in the given vec2
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the matrix to translate
- * @param {vec2} v the vec2 to translate the matrix by
- * @returns {mat2d} out
- **/
-function translate(out, a, v) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
-  let v0 = v[0], v1 = v[1];
-  out[0] = a0;
-  out[1] = a1;
-  out[2] = a2;
-  out[3] = a3;
-  out[4] = a0 * v0 + a2 * v1 + a4;
-  out[5] = a1 * v0 + a3 * v1 + a5;
-  return out;
-}
-
-/**
- * Creates a matrix from a given angle
- * This is equivalent to (but much faster than):
- *
- *     mat2d.identity(dest);
- *     mat2d.rotate(dest, dest, rad);
- *
- * @param {mat2d} out mat2d receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat2d} out
- */
-function fromRotation(out, rad) {
-  let s = Math.sin(rad), c = Math.cos(rad);
-  out[0] = c;
-  out[1] = s;
-  out[2] = -s;
-  out[3] = c;
-  out[4] = 0;
-  out[5] = 0;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector scaling
- * This is equivalent to (but much faster than):
- *
- *     mat2d.identity(dest);
- *     mat2d.scale(dest, dest, vec);
- *
- * @param {mat2d} out mat2d receiving operation result
- * @param {vec2} v Scaling vector
- * @returns {mat2d} out
- */
-function fromScaling(out, v) {
-  out[0] = v[0];
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = v[1];
-  out[4] = 0;
-  out[5] = 0;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector translation
- * This is equivalent to (but much faster than):
- *
- *     mat2d.identity(dest);
- *     mat2d.translate(dest, dest, vec);
- *
- * @param {mat2d} out mat2d receiving operation result
- * @param {vec2} v Translation vector
- * @returns {mat2d} out
- */
-function fromTranslation(out, v) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  out[4] = v[0];
-  out[5] = v[1];
-  return out;
-}
-
-/**
- * Returns a string representation of a mat2d
- *
- * @param {mat2d} a matrix to represent as a string
- * @returns {String} string representation of the matrix
- */
-function str(a) {
-  return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
-          a[3] + ', ' + a[4] + ', ' + a[5] + ')';
-}
-
-/**
- * Returns Frobenius norm of a mat2d
- *
- * @param {mat2d} a the matrix to calculate Frobenius norm of
- * @returns {Number} Frobenius norm
- */
-function frob(a) {
-  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1))
-}
-
-/**
- * Adds two mat2d's
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the first operand
- * @param {mat2d} b the second operand
- * @returns {mat2d} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  out[4] = a[4] + b[4];
-  out[5] = a[5] + b[5];
-  return out;
-}
-
-/**
- * Subtracts matrix b from matrix a
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the first operand
- * @param {mat2d} b the second operand
- * @returns {mat2d} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  out[3] = a[3] - b[3];
-  out[4] = a[4] - b[4];
-  out[5] = a[5] - b[5];
-  return out;
-}
-
-/**
- * Multiply each element of the matrix by a scalar.
- *
- * @param {mat2d} out the receiving matrix
- * @param {mat2d} a the matrix to scale
- * @param {Number} b amount to scale the matrix's elements by
- * @returns {mat2d} out
- */
-function multiplyScalar(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  out[4] = a[4] * b;
-  out[5] = a[5] * b;
-  return out;
-}
-
-/**
- * Adds two mat2d's after multiplying each element of the second operand by a scalar value.
- *
- * @param {mat2d} out the receiving vector
- * @param {mat2d} a the first operand
- * @param {mat2d} b the second operand
- * @param {Number} scale the amount to scale b's elements by before adding
- * @returns {mat2d} out
- */
-function multiplyScalarAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  out[3] = a[3] + (b[3] * scale);
-  out[4] = a[4] + (b[4] * scale);
-  out[5] = a[5] + (b[5] * scale);
-  return out;
-}
-
-/**
- * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
- *
- * @param {mat2d} a The first matrix.
- * @param {mat2d} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];
-}
-
-/**
- * Returns whether or not the matrices have approximately the same elements in the same position.
- *
- * @param {mat2d} a The first matrix.
- * @param {mat2d} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-          Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
-          Math.abs(a4 - b4) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
-          Math.abs(a5 - b5) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a5), Math.abs(b5)));
-}
-
-/**
- * Alias for {@link mat2d.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link mat2d.subtract}
- * @function
- */
-const sub = subtract;
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/mat3.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/mat3.js ***!
-  \******************************************************/
-/*! exports provided: create, fromMat4, clone, copy, fromValues, set, identity, transpose, invert, adjoint, determinant, multiply, translate, rotate, scale, fromTranslation, fromRotation, fromScaling, fromMat2d, fromQuat, normalFromMat4, projection, str, frob, add, subtract, multiplyScalar, multiplyScalarAndAdd, exactEquals, equals, mul, sub */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromMat4", function() { return fromMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transpose", function() { return transpose; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "adjoint", function() { return adjoint; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "determinant", function() { return determinant; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "translate", function() { return translate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromTranslation", function() { return fromTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotation", function() { return fromRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromScaling", function() { return fromScaling; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromMat2d", function() { return fromMat2d; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromQuat", function() { return fromQuat; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalFromMat4", function() { return normalFromMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "projection", function() { return projection; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "frob", function() { return frob; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalar", function() { return multiplyScalar; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalarAndAdd", function() { return multiplyScalarAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 3x3 Matrix
- * @module mat3
- */
-
-/**
- * Creates a new identity mat3
- *
- * @returns {mat3} a new 3x3 matrix
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](9);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[1] = 0;
-    out[2] = 0;
-    out[3] = 0;
-    out[5] = 0;
-    out[6] = 0;
-    out[7] = 0;
-  }
-  out[0] = 1;
-  out[4] = 1;
-  out[8] = 1;
-  return out;
-}
-
-/**
- * Copies the upper-left 3x3 values into the given mat3.
- *
- * @param {mat3} out the receiving 3x3 matrix
- * @param {mat4} a   the source 4x4 matrix
- * @returns {mat3} out
- */
-function fromMat4(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[4];
-  out[4] = a[5];
-  out[5] = a[6];
-  out[6] = a[8];
-  out[7] = a[9];
-  out[8] = a[10];
-  return out;
-}
-
-/**
- * Creates a new mat3 initialized with values from an existing matrix
- *
- * @param {mat3} a matrix to clone
- * @returns {mat3} a new 3x3 matrix
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](9);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  out[6] = a[6];
-  out[7] = a[7];
-  out[8] = a[8];
-  return out;
-}
-
-/**
- * Copy the values from one mat3 to another
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the source matrix
- * @returns {mat3} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  out[6] = a[6];
-  out[7] = a[7];
-  out[8] = a[8];
-  return out;
-}
-
-/**
- * Create a new mat3 with the given values
- *
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m02 Component in column 0, row 2 position (index 2)
- * @param {Number} m10 Component in column 1, row 0 position (index 3)
- * @param {Number} m11 Component in column 1, row 1 position (index 4)
- * @param {Number} m12 Component in column 1, row 2 position (index 5)
- * @param {Number} m20 Component in column 2, row 0 position (index 6)
- * @param {Number} m21 Component in column 2, row 1 position (index 7)
- * @param {Number} m22 Component in column 2, row 2 position (index 8)
- * @returns {mat3} A new mat3
- */
-function fromValues(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](9);
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m02;
-  out[3] = m10;
-  out[4] = m11;
-  out[5] = m12;
-  out[6] = m20;
-  out[7] = m21;
-  out[8] = m22;
-  return out;
-}
-
-/**
- * Set the components of a mat3 to the given values
- *
- * @param {mat3} out the receiving matrix
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m02 Component in column 0, row 2 position (index 2)
- * @param {Number} m10 Component in column 1, row 0 position (index 3)
- * @param {Number} m11 Component in column 1, row 1 position (index 4)
- * @param {Number} m12 Component in column 1, row 2 position (index 5)
- * @param {Number} m20 Component in column 2, row 0 position (index 6)
- * @param {Number} m21 Component in column 2, row 1 position (index 7)
- * @param {Number} m22 Component in column 2, row 2 position (index 8)
- * @returns {mat3} out
- */
-function set(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m02;
-  out[3] = m10;
-  out[4] = m11;
-  out[5] = m12;
-  out[6] = m20;
-  out[7] = m21;
-  out[8] = m22;
-  return out;
-}
-
-/**
- * Set a mat3 to the identity matrix
- *
- * @param {mat3} out the receiving matrix
- * @returns {mat3} out
- */
-function identity(out) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 1;
-  out[5] = 0;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 1;
-  return out;
-}
-
-/**
- * Transpose the values of a mat3
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the source matrix
- * @returns {mat3} out
- */
-function transpose(out, a) {
-  // If we are transposing ourselves we can skip a few steps but have to cache some values
-  if (out === a) {
-    let a01 = a[1], a02 = a[2], a12 = a[5];
-    out[1] = a[3];
-    out[2] = a[6];
-    out[3] = a01;
-    out[5] = a[7];
-    out[6] = a02;
-    out[7] = a12;
-  } else {
-    out[0] = a[0];
-    out[1] = a[3];
-    out[2] = a[6];
-    out[3] = a[1];
-    out[4] = a[4];
-    out[5] = a[7];
-    out[6] = a[2];
-    out[7] = a[5];
-    out[8] = a[8];
-  }
-
-  return out;
-}
-
-/**
- * Inverts a mat3
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the source matrix
- * @returns {mat3} out
- */
-function invert(out, a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2];
-  let a10 = a[3], a11 = a[4], a12 = a[5];
-  let a20 = a[6], a21 = a[7], a22 = a[8];
-
-  let b01 = a22 * a11 - a12 * a21;
-  let b11 = -a22 * a10 + a12 * a20;
-  let b21 = a21 * a10 - a11 * a20;
-
-  // Calculate the determinant
-  let det = a00 * b01 + a01 * b11 + a02 * b21;
-
-  if (!det) {
-    return null;
-  }
-  det = 1.0 / det;
-
-  out[0] = b01 * det;
-  out[1] = (-a22 * a01 + a02 * a21) * det;
-  out[2] = (a12 * a01 - a02 * a11) * det;
-  out[3] = b11 * det;
-  out[4] = (a22 * a00 - a02 * a20) * det;
-  out[5] = (-a12 * a00 + a02 * a10) * det;
-  out[6] = b21 * det;
-  out[7] = (-a21 * a00 + a01 * a20) * det;
-  out[8] = (a11 * a00 - a01 * a10) * det;
-  return out;
-}
-
-/**
- * Calculates the adjugate of a mat3
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the source matrix
- * @returns {mat3} out
- */
-function adjoint(out, a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2];
-  let a10 = a[3], a11 = a[4], a12 = a[5];
-  let a20 = a[6], a21 = a[7], a22 = a[8];
-
-  out[0] = (a11 * a22 - a12 * a21);
-  out[1] = (a02 * a21 - a01 * a22);
-  out[2] = (a01 * a12 - a02 * a11);
-  out[3] = (a12 * a20 - a10 * a22);
-  out[4] = (a00 * a22 - a02 * a20);
-  out[5] = (a02 * a10 - a00 * a12);
-  out[6] = (a10 * a21 - a11 * a20);
-  out[7] = (a01 * a20 - a00 * a21);
-  out[8] = (a00 * a11 - a01 * a10);
-  return out;
-}
-
-/**
- * Calculates the determinant of a mat3
- *
- * @param {mat3} a the source matrix
- * @returns {Number} determinant of a
- */
-function determinant(a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2];
-  let a10 = a[3], a11 = a[4], a12 = a[5];
-  let a20 = a[6], a21 = a[7], a22 = a[8];
-
-  return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
-}
-
-/**
- * Multiplies two mat3's
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the first operand
- * @param {mat3} b the second operand
- * @returns {mat3} out
- */
-function multiply(out, a, b) {
-  let a00 = a[0], a01 = a[1], a02 = a[2];
-  let a10 = a[3], a11 = a[4], a12 = a[5];
-  let a20 = a[6], a21 = a[7], a22 = a[8];
-
-  let b00 = b[0], b01 = b[1], b02 = b[2];
-  let b10 = b[3], b11 = b[4], b12 = b[5];
-  let b20 = b[6], b21 = b[7], b22 = b[8];
-
-  out[0] = b00 * a00 + b01 * a10 + b02 * a20;
-  out[1] = b00 * a01 + b01 * a11 + b02 * a21;
-  out[2] = b00 * a02 + b01 * a12 + b02 * a22;
-
-  out[3] = b10 * a00 + b11 * a10 + b12 * a20;
-  out[4] = b10 * a01 + b11 * a11 + b12 * a21;
-  out[5] = b10 * a02 + b11 * a12 + b12 * a22;
-
-  out[6] = b20 * a00 + b21 * a10 + b22 * a20;
-  out[7] = b20 * a01 + b21 * a11 + b22 * a21;
-  out[8] = b20 * a02 + b21 * a12 + b22 * a22;
-  return out;
-}
-
-/**
- * Translate a mat3 by the given vector
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the matrix to translate
- * @param {vec2} v vector to translate by
- * @returns {mat3} out
- */
-function translate(out, a, v) {
-  let a00 = a[0], a01 = a[1], a02 = a[2],
-    a10 = a[3], a11 = a[4], a12 = a[5],
-    a20 = a[6], a21 = a[7], a22 = a[8],
-    x = v[0], y = v[1];
-
-  out[0] = a00;
-  out[1] = a01;
-  out[2] = a02;
-
-  out[3] = a10;
-  out[4] = a11;
-  out[5] = a12;
-
-  out[6] = x * a00 + y * a10 + a20;
-  out[7] = x * a01 + y * a11 + a21;
-  out[8] = x * a02 + y * a12 + a22;
-  return out;
-}
-
-/**
- * Rotates a mat3 by the given angle
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat3} out
- */
-function rotate(out, a, rad) {
-  let a00 = a[0], a01 = a[1], a02 = a[2],
-    a10 = a[3], a11 = a[4], a12 = a[5],
-    a20 = a[6], a21 = a[7], a22 = a[8],
-
-    s = Math.sin(rad),
-    c = Math.cos(rad);
-
-  out[0] = c * a00 + s * a10;
-  out[1] = c * a01 + s * a11;
-  out[2] = c * a02 + s * a12;
-
-  out[3] = c * a10 - s * a00;
-  out[4] = c * a11 - s * a01;
-  out[5] = c * a12 - s * a02;
-
-  out[6] = a20;
-  out[7] = a21;
-  out[8] = a22;
-  return out;
-};
-
-/**
- * Scales the mat3 by the dimensions in the given vec2
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the matrix to rotate
- * @param {vec2} v the vec2 to scale the matrix by
- * @returns {mat3} out
- **/
-function scale(out, a, v) {
-  let x = v[0], y = v[1];
-
-  out[0] = x * a[0];
-  out[1] = x * a[1];
-  out[2] = x * a[2];
-
-  out[3] = y * a[3];
-  out[4] = y * a[4];
-  out[5] = y * a[5];
-
-  out[6] = a[6];
-  out[7] = a[7];
-  out[8] = a[8];
-  return out;
-}
-
-/**
- * Creates a matrix from a vector translation
- * This is equivalent to (but much faster than):
- *
- *     mat3.identity(dest);
- *     mat3.translate(dest, dest, vec);
- *
- * @param {mat3} out mat3 receiving operation result
- * @param {vec2} v Translation vector
- * @returns {mat3} out
- */
-function fromTranslation(out, v) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 1;
-  out[5] = 0;
-  out[6] = v[0];
-  out[7] = v[1];
-  out[8] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from a given angle
- * This is equivalent to (but much faster than):
- *
- *     mat3.identity(dest);
- *     mat3.rotate(dest, dest, rad);
- *
- * @param {mat3} out mat3 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat3} out
- */
-function fromRotation(out, rad) {
-  let s = Math.sin(rad), c = Math.cos(rad);
-
-  out[0] = c;
-  out[1] = s;
-  out[2] = 0;
-
-  out[3] = -s;
-  out[4] = c;
-  out[5] = 0;
-
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector scaling
- * This is equivalent to (but much faster than):
- *
- *     mat3.identity(dest);
- *     mat3.scale(dest, dest, vec);
- *
- * @param {mat3} out mat3 receiving operation result
- * @param {vec2} v Scaling vector
- * @returns {mat3} out
- */
-function fromScaling(out, v) {
-  out[0] = v[0];
-  out[1] = 0;
-  out[2] = 0;
-
-  out[3] = 0;
-  out[4] = v[1];
-  out[5] = 0;
-
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 1;
-  return out;
-}
-
-/**
- * Copies the values from a mat2d into a mat3
- *
- * @param {mat3} out the receiving matrix
- * @param {mat2d} a the matrix to copy
- * @returns {mat3} out
- **/
-function fromMat2d(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = 0;
-
-  out[3] = a[2];
-  out[4] = a[3];
-  out[5] = 0;
-
-  out[6] = a[4];
-  out[7] = a[5];
-  out[8] = 1;
-  return out;
-}
-
-/**
-* Calculates a 3x3 matrix from the given quaternion
-*
-* @param {mat3} out mat3 receiving operation result
-* @param {quat} q Quaternion to create matrix from
-*
-* @returns {mat3} out
-*/
-function fromQuat(out, q) {
-  let x = q[0], y = q[1], z = q[2], w = q[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-
-  let xx = x * x2;
-  let yx = y * x2;
-  let yy = y * y2;
-  let zx = z * x2;
-  let zy = z * y2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-
-  out[0] = 1 - yy - zz;
-  out[3] = yx - wz;
-  out[6] = zx + wy;
-
-  out[1] = yx + wz;
-  out[4] = 1 - xx - zz;
-  out[7] = zy - wx;
-
-  out[2] = zx - wy;
-  out[5] = zy + wx;
-  out[8] = 1 - xx - yy;
-
-  return out;
-}
-
-/**
-* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix
-*
-* @param {mat3} out mat3 receiving operation result
-* @param {mat4} a Mat4 to derive the normal matrix from
-*
-* @returns {mat3} out
-*/
-function normalFromMat4(out, a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
-  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
-  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
-  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
-  let b00 = a00 * a11 - a01 * a10;
-  let b01 = a00 * a12 - a02 * a10;
-  let b02 = a00 * a13 - a03 * a10;
-  let b03 = a01 * a12 - a02 * a11;
-  let b04 = a01 * a13 - a03 * a11;
-  let b05 = a02 * a13 - a03 * a12;
-  let b06 = a20 * a31 - a21 * a30;
-  let b07 = a20 * a32 - a22 * a30;
-  let b08 = a20 * a33 - a23 * a30;
-  let b09 = a21 * a32 - a22 * a31;
-  let b10 = a21 * a33 - a23 * a31;
-  let b11 = a22 * a33 - a23 * a32;
-
-  // Calculate the determinant
-  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
-
-  if (!det) {
-    return null;
-  }
-  det = 1.0 / det;
-
-  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
-  out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
-  out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
-
-  out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
-  out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
-  out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
-
-  out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
-  out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
-  out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
-
-  return out;
-}
-
-/**
- * Generates a 2D projection matrix with the given bounds
- *
- * @param {mat3} out mat3 frustum matrix will be written into
- * @param {number} width Width of your gl context
- * @param {number} height Height of gl context
- * @returns {mat3} out
- */
-function projection(out, width, height) {
-    out[0] = 2 / width;
-    out[1] = 0;
-    out[2] = 0;
-    out[3] = 0;
-    out[4] = -2 / height;
-    out[5] = 0;
-    out[6] = -1;
-    out[7] = 1;
-    out[8] = 1;
-    return out;
-}
-
-/**
- * Returns a string representation of a mat3
- *
- * @param {mat3} a matrix to represent as a string
- * @returns {String} string representation of the matrix
- */
-function str(a) {
-  return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
-          a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +
-          a[6] + ', ' + a[7] + ', ' + a[8] + ')';
-}
-
-/**
- * Returns Frobenius norm of a mat3
- *
- * @param {mat3} a the matrix to calculate Frobenius norm of
- * @returns {Number} Frobenius norm
- */
-function frob(a) {
-  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2)))
-}
-
-/**
- * Adds two mat3's
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the first operand
- * @param {mat3} b the second operand
- * @returns {mat3} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  out[4] = a[4] + b[4];
-  out[5] = a[5] + b[5];
-  out[6] = a[6] + b[6];
-  out[7] = a[7] + b[7];
-  out[8] = a[8] + b[8];
-  return out;
-}
-
-/**
- * Subtracts matrix b from matrix a
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the first operand
- * @param {mat3} b the second operand
- * @returns {mat3} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  out[3] = a[3] - b[3];
-  out[4] = a[4] - b[4];
-  out[5] = a[5] - b[5];
-  out[6] = a[6] - b[6];
-  out[7] = a[7] - b[7];
-  out[8] = a[8] - b[8];
-  return out;
-}
-
-
-
-/**
- * Multiply each element of the matrix by a scalar.
- *
- * @param {mat3} out the receiving matrix
- * @param {mat3} a the matrix to scale
- * @param {Number} b amount to scale the matrix's elements by
- * @returns {mat3} out
- */
-function multiplyScalar(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  out[4] = a[4] * b;
-  out[5] = a[5] * b;
-  out[6] = a[6] * b;
-  out[7] = a[7] * b;
-  out[8] = a[8] * b;
-  return out;
-}
-
-/**
- * Adds two mat3's after multiplying each element of the second operand by a scalar value.
- *
- * @param {mat3} out the receiving vector
- * @param {mat3} a the first operand
- * @param {mat3} b the second operand
- * @param {Number} scale the amount to scale b's elements by before adding
- * @returns {mat3} out
- */
-function multiplyScalarAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  out[3] = a[3] + (b[3] * scale);
-  out[4] = a[4] + (b[4] * scale);
-  out[5] = a[5] + (b[5] * scale);
-  out[6] = a[6] + (b[6] * scale);
-  out[7] = a[7] + (b[7] * scale);
-  out[8] = a[8] + (b[8] * scale);
-  return out;
-}
-
-/**
- * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
- *
- * @param {mat3} a The first matrix.
- * @param {mat3} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] &&
-         a[3] === b[3] && a[4] === b[4] && a[5] === b[5] &&
-         a[6] === b[6] && a[7] === b[7] && a[8] === b[8];
-}
-
-/**
- * Returns whether or not the matrices have approximately the same elements in the same position.
- *
- * @param {mat3} a The first matrix.
- * @param {mat3} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7], a8 = a[8];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-          Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
-          Math.abs(a4 - b4) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
-          Math.abs(a5 - b5) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
-          Math.abs(a6 - b6) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
-          Math.abs(a7 - b7) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
-          Math.abs(a8 - b8) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a8), Math.abs(b8)));
-}
-
-/**
- * Alias for {@link mat3.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link mat3.subtract}
- * @function
- */
-const sub = subtract;
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/mat4.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/mat4.js ***!
-  \******************************************************/
-/*! exports provided: create, clone, copy, fromValues, set, identity, transpose, invert, adjoint, determinant, multiply, translate, scale, rotate, rotateX, rotateY, rotateZ, fromTranslation, fromScaling, fromRotation, fromXRotation, fromYRotation, fromZRotation, fromRotationTranslation, fromQuat2, getTranslation, getScaling, getRotation, fromRotationTranslationScale, fromRotationTranslationScaleOrigin, fromQuat, frustum, perspective, perspectiveFromFieldOfView, ortho, lookAt, targetTo, str, frob, add, subtract, multiplyScalar, multiplyScalarAndAdd, exactEquals, equals, mul, sub */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transpose", function() { return transpose; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "adjoint", function() { return adjoint; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "determinant", function() { return determinant; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "translate", function() { return translate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateX", function() { return rotateX; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateY", function() { return rotateY; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateZ", function() { return rotateZ; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromTranslation", function() { return fromTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromScaling", function() { return fromScaling; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotation", function() { return fromRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromXRotation", function() { return fromXRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromYRotation", function() { return fromYRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromZRotation", function() { return fromZRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotationTranslation", function() { return fromRotationTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromQuat2", function() { return fromQuat2; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getTranslation", function() { return getTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getScaling", function() { return getScaling; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getRotation", function() { return getRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotationTranslationScale", function() { return fromRotationTranslationScale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotationTranslationScaleOrigin", function() { return fromRotationTranslationScaleOrigin; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromQuat", function() { return fromQuat; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "frustum", function() { return frustum; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "perspective", function() { return perspective; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "perspectiveFromFieldOfView", function() { return perspectiveFromFieldOfView; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ortho", function() { return ortho; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lookAt", function() { return lookAt; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "targetTo", function() { return targetTo; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "frob", function() { return frob; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalar", function() { return multiplyScalar; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiplyScalarAndAdd", function() { return multiplyScalarAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
- * @module mat4
- */
-
-/**
- * Creates a new identity mat4
- *
- * @returns {mat4} a new 4x4 matrix
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](16);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[1] = 0;
-    out[2] = 0;
-    out[3] = 0;
-    out[4] = 0;
-    out[6] = 0;
-    out[7] = 0;
-    out[8] = 0;
-    out[9] = 0;
-    out[11] = 0;
-    out[12] = 0;
-    out[13] = 0;
-    out[14] = 0;
-  }
-  out[0] = 1;
-  out[5] = 1;
-  out[10] = 1;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a new mat4 initialized with values from an existing matrix
- *
- * @param {mat4} a matrix to clone
- * @returns {mat4} a new 4x4 matrix
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](16);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  out[6] = a[6];
-  out[7] = a[7];
-  out[8] = a[8];
-  out[9] = a[9];
-  out[10] = a[10];
-  out[11] = a[11];
-  out[12] = a[12];
-  out[13] = a[13];
-  out[14] = a[14];
-  out[15] = a[15];
-  return out;
-}
-
-/**
- * Copy the values from one mat4 to another
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the source matrix
- * @returns {mat4} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  out[6] = a[6];
-  out[7] = a[7];
-  out[8] = a[8];
-  out[9] = a[9];
-  out[10] = a[10];
-  out[11] = a[11];
-  out[12] = a[12];
-  out[13] = a[13];
-  out[14] = a[14];
-  out[15] = a[15];
-  return out;
-}
-
-/**
- * Create a new mat4 with the given values
- *
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m02 Component in column 0, row 2 position (index 2)
- * @param {Number} m03 Component in column 0, row 3 position (index 3)
- * @param {Number} m10 Component in column 1, row 0 position (index 4)
- * @param {Number} m11 Component in column 1, row 1 position (index 5)
- * @param {Number} m12 Component in column 1, row 2 position (index 6)
- * @param {Number} m13 Component in column 1, row 3 position (index 7)
- * @param {Number} m20 Component in column 2, row 0 position (index 8)
- * @param {Number} m21 Component in column 2, row 1 position (index 9)
- * @param {Number} m22 Component in column 2, row 2 position (index 10)
- * @param {Number} m23 Component in column 2, row 3 position (index 11)
- * @param {Number} m30 Component in column 3, row 0 position (index 12)
- * @param {Number} m31 Component in column 3, row 1 position (index 13)
- * @param {Number} m32 Component in column 3, row 2 position (index 14)
- * @param {Number} m33 Component in column 3, row 3 position (index 15)
- * @returns {mat4} A new mat4
- */
-function fromValues(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](16);
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m02;
-  out[3] = m03;
-  out[4] = m10;
-  out[5] = m11;
-  out[6] = m12;
-  out[7] = m13;
-  out[8] = m20;
-  out[9] = m21;
-  out[10] = m22;
-  out[11] = m23;
-  out[12] = m30;
-  out[13] = m31;
-  out[14] = m32;
-  out[15] = m33;
-  return out;
-}
-
-/**
- * Set the components of a mat4 to the given values
- *
- * @param {mat4} out the receiving matrix
- * @param {Number} m00 Component in column 0, row 0 position (index 0)
- * @param {Number} m01 Component in column 0, row 1 position (index 1)
- * @param {Number} m02 Component in column 0, row 2 position (index 2)
- * @param {Number} m03 Component in column 0, row 3 position (index 3)
- * @param {Number} m10 Component in column 1, row 0 position (index 4)
- * @param {Number} m11 Component in column 1, row 1 position (index 5)
- * @param {Number} m12 Component in column 1, row 2 position (index 6)
- * @param {Number} m13 Component in column 1, row 3 position (index 7)
- * @param {Number} m20 Component in column 2, row 0 position (index 8)
- * @param {Number} m21 Component in column 2, row 1 position (index 9)
- * @param {Number} m22 Component in column 2, row 2 position (index 10)
- * @param {Number} m23 Component in column 2, row 3 position (index 11)
- * @param {Number} m30 Component in column 3, row 0 position (index 12)
- * @param {Number} m31 Component in column 3, row 1 position (index 13)
- * @param {Number} m32 Component in column 3, row 2 position (index 14)
- * @param {Number} m33 Component in column 3, row 3 position (index 15)
- * @returns {mat4} out
- */
-function set(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
-  out[0] = m00;
-  out[1] = m01;
-  out[2] = m02;
-  out[3] = m03;
-  out[4] = m10;
-  out[5] = m11;
-  out[6] = m12;
-  out[7] = m13;
-  out[8] = m20;
-  out[9] = m21;
-  out[10] = m22;
-  out[11] = m23;
-  out[12] = m30;
-  out[13] = m31;
-  out[14] = m32;
-  out[15] = m33;
-  return out;
-}
-
-
-/**
- * Set a mat4 to the identity matrix
- *
- * @param {mat4} out the receiving matrix
- * @returns {mat4} out
- */
-function identity(out) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = 1;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[10] = 1;
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Transpose the values of a mat4
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the source matrix
- * @returns {mat4} out
- */
-function transpose(out, a) {
-  // If we are transposing ourselves we can skip a few steps but have to cache some values
-  if (out === a) {
-    let a01 = a[1], a02 = a[2], a03 = a[3];
-    let a12 = a[6], a13 = a[7];
-    let a23 = a[11];
-
-    out[1] = a[4];
-    out[2] = a[8];
-    out[3] = a[12];
-    out[4] = a01;
-    out[6] = a[9];
-    out[7] = a[13];
-    out[8] = a02;
-    out[9] = a12;
-    out[11] = a[14];
-    out[12] = a03;
-    out[13] = a13;
-    out[14] = a23;
-  } else {
-    out[0] = a[0];
-    out[1] = a[4];
-    out[2] = a[8];
-    out[3] = a[12];
-    out[4] = a[1];
-    out[5] = a[5];
-    out[6] = a[9];
-    out[7] = a[13];
-    out[8] = a[2];
-    out[9] = a[6];
-    out[10] = a[10];
-    out[11] = a[14];
-    out[12] = a[3];
-    out[13] = a[7];
-    out[14] = a[11];
-    out[15] = a[15];
-  }
-
-  return out;
-}
-
-/**
- * Inverts a mat4
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the source matrix
- * @returns {mat4} out
- */
-function invert(out, a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
-  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
-  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
-  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
-  let b00 = a00 * a11 - a01 * a10;
-  let b01 = a00 * a12 - a02 * a10;
-  let b02 = a00 * a13 - a03 * a10;
-  let b03 = a01 * a12 - a02 * a11;
-  let b04 = a01 * a13 - a03 * a11;
-  let b05 = a02 * a13 - a03 * a12;
-  let b06 = a20 * a31 - a21 * a30;
-  let b07 = a20 * a32 - a22 * a30;
-  let b08 = a20 * a33 - a23 * a30;
-  let b09 = a21 * a32 - a22 * a31;
-  let b10 = a21 * a33 - a23 * a31;
-  let b11 = a22 * a33 - a23 * a32;
-
-  // Calculate the determinant
-  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
-
-  if (!det) {
-    return null;
-  }
-  det = 1.0 / det;
-
-  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
-  out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
-  out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
-  out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
-  out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
-  out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
-  out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
-  out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
-  out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
-  out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
-  out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
-  out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
-  out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
-  out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
-  out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
-  out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
-
-  return out;
-}
-
-/**
- * Calculates the adjugate of a mat4
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the source matrix
- * @returns {mat4} out
- */
-function adjoint(out, a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
-  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
-  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
-  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
-  out[0]  =  (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
-  out[1]  = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
-  out[2]  =  (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
-  out[3]  = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
-  out[4]  = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
-  out[5]  =  (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
-  out[6]  = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
-  out[7]  =  (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
-  out[8]  =  (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
-  out[9]  = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
-  out[10] =  (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
-  out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
-  out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
-  out[13] =  (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
-  out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
-  out[15] =  (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
-  return out;
-}
-
-/**
- * Calculates the determinant of a mat4
- *
- * @param {mat4} a the source matrix
- * @returns {Number} determinant of a
- */
-function determinant(a) {
-  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
-  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
-  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
-  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
-  let b00 = a00 * a11 - a01 * a10;
-  let b01 = a00 * a12 - a02 * a10;
-  let b02 = a00 * a13 - a03 * a10;
-  let b03 = a01 * a12 - a02 * a11;
-  let b04 = a01 * a13 - a03 * a11;
-  let b05 = a02 * a13 - a03 * a12;
-  let b06 = a20 * a31 - a21 * a30;
-  let b07 = a20 * a32 - a22 * a30;
-  let b08 = a20 * a33 - a23 * a30;
-  let b09 = a21 * a32 - a22 * a31;
-  let b10 = a21 * a33 - a23 * a31;
-  let b11 = a22 * a33 - a23 * a32;
-
-  // Calculate the determinant
-  return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
-}
-
-/**
- * Multiplies two mat4s
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the first operand
- * @param {mat4} b the second operand
- * @returns {mat4} out
- */
-function multiply(out, a, b) {
-  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
-  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
-  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
-  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
-  // Cache only the current line of the second matrix
-  let b0  = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
-  out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
-  out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
-  out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
-  out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
-  b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
-  out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
-  out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
-  out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
-  out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
-  b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
-  out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
-  out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
-  out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
-  out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
-  b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
-  out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
-  out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
-  out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
-  out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-  return out;
-}
-
-/**
- * Translate a mat4 by the given vector
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to translate
- * @param {vec3} v vector to translate by
- * @returns {mat4} out
- */
-function translate(out, a, v) {
-  let x = v[0], y = v[1], z = v[2];
-  let a00, a01, a02, a03;
-  let a10, a11, a12, a13;
-  let a20, a21, a22, a23;
-
-  if (a === out) {
-    out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
-    out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
-    out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
-    out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
-  } else {
-    a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
-    a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
-    a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
-
-    out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
-    out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
-    out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
-
-    out[12] = a00 * x + a10 * y + a20 * z + a[12];
-    out[13] = a01 * x + a11 * y + a21 * z + a[13];
-    out[14] = a02 * x + a12 * y + a22 * z + a[14];
-    out[15] = a03 * x + a13 * y + a23 * z + a[15];
-  }
-
-  return out;
-}
-
-/**
- * Scales the mat4 by the dimensions in the given vec3 not using vectorization
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to scale
- * @param {vec3} v the vec3 to scale the matrix by
- * @returns {mat4} out
- **/
-function scale(out, a, v) {
-  let x = v[0], y = v[1], z = v[2];
-
-  out[0] = a[0] * x;
-  out[1] = a[1] * x;
-  out[2] = a[2] * x;
-  out[3] = a[3] * x;
-  out[4] = a[4] * y;
-  out[5] = a[5] * y;
-  out[6] = a[6] * y;
-  out[7] = a[7] * y;
-  out[8] = a[8] * z;
-  out[9] = a[9] * z;
-  out[10] = a[10] * z;
-  out[11] = a[11] * z;
-  out[12] = a[12];
-  out[13] = a[13];
-  out[14] = a[14];
-  out[15] = a[15];
-  return out;
-}
-
-/**
- * Rotates a mat4 by the given angle around the given axis
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @param {vec3} axis the axis to rotate around
- * @returns {mat4} out
- */
-function rotate(out, a, rad, axis) {
-  let x = axis[0], y = axis[1], z = axis[2];
-  let len = Math.sqrt(x * x + y * y + z * z);
-  let s, c, t;
-  let a00, a01, a02, a03;
-  let a10, a11, a12, a13;
-  let a20, a21, a22, a23;
-  let b00, b01, b02;
-  let b10, b11, b12;
-  let b20, b21, b22;
-
-  if (len < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]) { return null; }
-
-  len = 1 / len;
-  x *= len;
-  y *= len;
-  z *= len;
-
-  s = Math.sin(rad);
-  c = Math.cos(rad);
-  t = 1 - c;
-
-  a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
-  a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
-  a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
-
-  // Construct the elements of the rotation matrix
-  b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
-  b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
-  b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
-
-  // Perform rotation-specific matrix multiplication
-  out[0] = a00 * b00 + a10 * b01 + a20 * b02;
-  out[1] = a01 * b00 + a11 * b01 + a21 * b02;
-  out[2] = a02 * b00 + a12 * b01 + a22 * b02;
-  out[3] = a03 * b00 + a13 * b01 + a23 * b02;
-  out[4] = a00 * b10 + a10 * b11 + a20 * b12;
-  out[5] = a01 * b10 + a11 * b11 + a21 * b12;
-  out[6] = a02 * b10 + a12 * b11 + a22 * b12;
-  out[7] = a03 * b10 + a13 * b11 + a23 * b12;
-  out[8] = a00 * b20 + a10 * b21 + a20 * b22;
-  out[9] = a01 * b20 + a11 * b21 + a21 * b22;
-  out[10] = a02 * b20 + a12 * b21 + a22 * b22;
-  out[11] = a03 * b20 + a13 * b21 + a23 * b22;
-
-  if (a !== out) { // If the source and destination differ, copy the unchanged last row
-    out[12] = a[12];
-    out[13] = a[13];
-    out[14] = a[14];
-    out[15] = a[15];
-  }
-  return out;
-}
-
-/**
- * Rotates a matrix by the given angle around the X axis
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function rotateX(out, a, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  let a10 = a[4];
-  let a11 = a[5];
-  let a12 = a[6];
-  let a13 = a[7];
-  let a20 = a[8];
-  let a21 = a[9];
-  let a22 = a[10];
-  let a23 = a[11];
-
-  if (a !== out) { // If the source and destination differ, copy the unchanged rows
-    out[0]  = a[0];
-    out[1]  = a[1];
-    out[2]  = a[2];
-    out[3]  = a[3];
-    out[12] = a[12];
-    out[13] = a[13];
-    out[14] = a[14];
-    out[15] = a[15];
-  }
-
-  // Perform axis-specific matrix multiplication
-  out[4] = a10 * c + a20 * s;
-  out[5] = a11 * c + a21 * s;
-  out[6] = a12 * c + a22 * s;
-  out[7] = a13 * c + a23 * s;
-  out[8] = a20 * c - a10 * s;
-  out[9] = a21 * c - a11 * s;
-  out[10] = a22 * c - a12 * s;
-  out[11] = a23 * c - a13 * s;
-  return out;
-}
-
-/**
- * Rotates a matrix by the given angle around the Y axis
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function rotateY(out, a, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  let a00 = a[0];
-  let a01 = a[1];
-  let a02 = a[2];
-  let a03 = a[3];
-  let a20 = a[8];
-  let a21 = a[9];
-  let a22 = a[10];
-  let a23 = a[11];
-
-  if (a !== out) { // If the source and destination differ, copy the unchanged rows
-    out[4]  = a[4];
-    out[5]  = a[5];
-    out[6]  = a[6];
-    out[7]  = a[7];
-    out[12] = a[12];
-    out[13] = a[13];
-    out[14] = a[14];
-    out[15] = a[15];
-  }
-
-  // Perform axis-specific matrix multiplication
-  out[0] = a00 * c - a20 * s;
-  out[1] = a01 * c - a21 * s;
-  out[2] = a02 * c - a22 * s;
-  out[3] = a03 * c - a23 * s;
-  out[8] = a00 * s + a20 * c;
-  out[9] = a01 * s + a21 * c;
-  out[10] = a02 * s + a22 * c;
-  out[11] = a03 * s + a23 * c;
-  return out;
-}
-
-/**
- * Rotates a matrix by the given angle around the Z axis
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to rotate
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function rotateZ(out, a, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-  let a00 = a[0];
-  let a01 = a[1];
-  let a02 = a[2];
-  let a03 = a[3];
-  let a10 = a[4];
-  let a11 = a[5];
-  let a12 = a[6];
-  let a13 = a[7];
-
-  if (a !== out) { // If the source and destination differ, copy the unchanged last row
-    out[8]  = a[8];
-    out[9]  = a[9];
-    out[10] = a[10];
-    out[11] = a[11];
-    out[12] = a[12];
-    out[13] = a[13];
-    out[14] = a[14];
-    out[15] = a[15];
-  }
-
-  // Perform axis-specific matrix multiplication
-  out[0] = a00 * c + a10 * s;
-  out[1] = a01 * c + a11 * s;
-  out[2] = a02 * c + a12 * s;
-  out[3] = a03 * c + a13 * s;
-  out[4] = a10 * c - a00 * s;
-  out[5] = a11 * c - a01 * s;
-  out[6] = a12 * c - a02 * s;
-  out[7] = a13 * c - a03 * s;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector translation
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.translate(dest, dest, vec);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {vec3} v Translation vector
- * @returns {mat4} out
- */
-function fromTranslation(out, v) {
-  out[0] = 1;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = 1;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[10] = 1;
-  out[11] = 0;
-  out[12] = v[0];
-  out[13] = v[1];
-  out[14] = v[2];
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from a vector scaling
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.scale(dest, dest, vec);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {vec3} v Scaling vector
- * @returns {mat4} out
- */
-function fromScaling(out, v) {
-  out[0] = v[0];
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = v[1];
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[10] = v[2];
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from a given angle around a given axis
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.rotate(dest, dest, rad, axis);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @param {vec3} axis the axis to rotate around
- * @returns {mat4} out
- */
-function fromRotation(out, rad, axis) {
-  let x = axis[0], y = axis[1], z = axis[2];
-  let len = Math.sqrt(x * x + y * y + z * z);
-  let s, c, t;
-
-  if (len < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]) { return null; }
-
-  len = 1 / len;
-  x *= len;
-  y *= len;
-  z *= len;
-
-  s = Math.sin(rad);
-  c = Math.cos(rad);
-  t = 1 - c;
-
-  // Perform rotation-specific matrix multiplication
-  out[0] = x * x * t + c;
-  out[1] = y * x * t + z * s;
-  out[2] = z * x * t - y * s;
-  out[3] = 0;
-  out[4] = x * y * t - z * s;
-  out[5] = y * y * t + c;
-  out[6] = z * y * t + x * s;
-  out[7] = 0;
-  out[8] = x * z * t + y * s;
-  out[9] = y * z * t - x * s;
-  out[10] = z * z * t + c;
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from the given angle around the X axis
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.rotateX(dest, dest, rad);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function fromXRotation(out, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-
-  // Perform axis-specific matrix multiplication
-  out[0]  = 1;
-  out[1]  = 0;
-  out[2]  = 0;
-  out[3]  = 0;
-  out[4] = 0;
-  out[5] = c;
-  out[6] = s;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = -s;
-  out[10] = c;
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from the given angle around the Y axis
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.rotateY(dest, dest, rad);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function fromYRotation(out, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-
-  // Perform axis-specific matrix multiplication
-  out[0]  = c;
-  out[1]  = 0;
-  out[2]  = -s;
-  out[3]  = 0;
-  out[4] = 0;
-  out[5] = 1;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = s;
-  out[9] = 0;
-  out[10] = c;
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from the given angle around the Z axis
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.rotateZ(dest, dest, rad);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {Number} rad the angle to rotate the matrix by
- * @returns {mat4} out
- */
-function fromZRotation(out, rad) {
-  let s = Math.sin(rad);
-  let c = Math.cos(rad);
-
-  // Perform axis-specific matrix multiplication
-  out[0]  = c;
-  out[1]  = s;
-  out[2]  = 0;
-  out[3]  = 0;
-  out[4] = -s;
-  out[5] = c;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[10] = 1;
-  out[11] = 0;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Creates a matrix from a quaternion rotation and vector translation
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.translate(dest, vec);
- *     let quatMat = mat4.create();
- *     quat4.toMat4(quat, quatMat);
- *     mat4.multiply(dest, quatMat);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {quat4} q Rotation quaternion
- * @param {vec3} v Translation vector
- * @returns {mat4} out
- */
-function fromRotationTranslation(out, q, v) {
-  // Quaternion math
-  let x = q[0], y = q[1], z = q[2], w = q[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-
-  let xx = x * x2;
-  let xy = x * y2;
-  let xz = x * z2;
-  let yy = y * y2;
-  let yz = y * z2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-
-  out[0] = 1 - (yy + zz);
-  out[1] = xy + wz;
-  out[2] = xz - wy;
-  out[3] = 0;
-  out[4] = xy - wz;
-  out[5] = 1 - (xx + zz);
-  out[6] = yz + wx;
-  out[7] = 0;
-  out[8] = xz + wy;
-  out[9] = yz - wx;
-  out[10] = 1 - (xx + yy);
-  out[11] = 0;
-  out[12] = v[0];
-  out[13] = v[1];
-  out[14] = v[2];
-  out[15] = 1;
-
-  return out;
-}
-
-/**
- * Creates a new mat4 from a dual quat.
- *
- * @param {mat4} out Matrix
- * @param {quat2} a Dual Quaternion
- * @returns {mat4} mat4 receiving operation result
- */
-function fromQuat2(out, a) {
-  let translation = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](3);
-  let bx = -a[0], by = -a[1], bz = -a[2], bw = a[3],
-  ax = a[4], ay = a[5], az = a[6], aw = a[7];
-
-  let magnitude = bx * bx + by * by + bz * bz + bw * bw;
-  //Only scale if it makes sense
-  if (magnitude > 0) {
-    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
-    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
-    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
-  } else {
-    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
-    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
-    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
-  }
-  fromRotationTranslation(out, a, translation);
-  return out;
-}
-
-/**
- * Returns the translation vector component of a transformation
- *  matrix. If a matrix is built with fromRotationTranslation,
- *  the returned vector will be the same as the translation vector
- *  originally supplied.
- * @param  {vec3} out Vector to receive translation component
- * @param  {mat4} mat Matrix to be decomposed (input)
- * @return {vec3} out
- */
-function getTranslation(out, mat) {
-  out[0] = mat[12];
-  out[1] = mat[13];
-  out[2] = mat[14];
-
-  return out;
-}
-
-/**
- * Returns the scaling factor component of a transformation
- *  matrix. If a matrix is built with fromRotationTranslationScale
- *  with a normalized Quaternion paramter, the returned vector will be
- *  the same as the scaling vector
- *  originally supplied.
- * @param  {vec3} out Vector to receive scaling factor component
- * @param  {mat4} mat Matrix to be decomposed (input)
- * @return {vec3} out
- */
-function getScaling(out, mat) {
-  let m11 = mat[0];
-  let m12 = mat[1];
-  let m13 = mat[2];
-  let m21 = mat[4];
-  let m22 = mat[5];
-  let m23 = mat[6];
-  let m31 = mat[8];
-  let m32 = mat[9];
-  let m33 = mat[10];
-
-  out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
-  out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
-  out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
-
-  return out;
-}
-
-/**
- * Returns a quaternion representing the rotational component
- *  of a transformation matrix. If a matrix is built with
- *  fromRotationTranslation, the returned quaternion will be the
- *  same as the quaternion originally supplied.
- * @param {quat} out Quaternion to receive the rotation component
- * @param {mat4} mat Matrix to be decomposed (input)
- * @return {quat} out
- */
-function getRotation(out, mat) {
-  // Algorithm taken from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-  let trace = mat[0] + mat[5] + mat[10];
-  let S = 0;
-
-  if (trace > 0) {
-    S = Math.sqrt(trace + 1.0) * 2;
-    out[3] = 0.25 * S;
-    out[0] = (mat[6] - mat[9]) / S;
-    out[1] = (mat[8] - mat[2]) / S;
-    out[2] = (mat[1] - mat[4]) / S;
-  } else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) {
-    S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2;
-    out[3] = (mat[6] - mat[9]) / S;
-    out[0] = 0.25 * S;
-    out[1] = (mat[1] + mat[4]) / S;
-    out[2] = (mat[8] + mat[2]) / S;
-  } else if (mat[5] > mat[10]) {
-    S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2;
-    out[3] = (mat[8] - mat[2]) / S;
-    out[0] = (mat[1] + mat[4]) / S;
-    out[1] = 0.25 * S;
-    out[2] = (mat[6] + mat[9]) / S;
-  } else {
-    S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2;
-    out[3] = (mat[1] - mat[4]) / S;
-    out[0] = (mat[8] + mat[2]) / S;
-    out[1] = (mat[6] + mat[9]) / S;
-    out[2] = 0.25 * S;
-  }
-
-  return out;
-}
-
-/**
- * Creates a matrix from a quaternion rotation, vector translation and vector scale
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.translate(dest, vec);
- *     let quatMat = mat4.create();
- *     quat4.toMat4(quat, quatMat);
- *     mat4.multiply(dest, quatMat);
- *     mat4.scale(dest, scale)
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {quat4} q Rotation quaternion
- * @param {vec3} v Translation vector
- * @param {vec3} s Scaling vector
- * @returns {mat4} out
- */
-function fromRotationTranslationScale(out, q, v, s) {
-  // Quaternion math
-  let x = q[0], y = q[1], z = q[2], w = q[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-
-  let xx = x * x2;
-  let xy = x * y2;
-  let xz = x * z2;
-  let yy = y * y2;
-  let yz = y * z2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-  let sx = s[0];
-  let sy = s[1];
-  let sz = s[2];
-
-  out[0] = (1 - (yy + zz)) * sx;
-  out[1] = (xy + wz) * sx;
-  out[2] = (xz - wy) * sx;
-  out[3] = 0;
-  out[4] = (xy - wz) * sy;
-  out[5] = (1 - (xx + zz)) * sy;
-  out[6] = (yz + wx) * sy;
-  out[7] = 0;
-  out[8] = (xz + wy) * sz;
-  out[9] = (yz - wx) * sz;
-  out[10] = (1 - (xx + yy)) * sz;
-  out[11] = 0;
-  out[12] = v[0];
-  out[13] = v[1];
-  out[14] = v[2];
-  out[15] = 1;
-
-  return out;
-}
-
-/**
- * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin
- * This is equivalent to (but much faster than):
- *
- *     mat4.identity(dest);
- *     mat4.translate(dest, vec);
- *     mat4.translate(dest, origin);
- *     let quatMat = mat4.create();
- *     quat4.toMat4(quat, quatMat);
- *     mat4.multiply(dest, quatMat);
- *     mat4.scale(dest, scale)
- *     mat4.translate(dest, negativeOrigin);
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {quat4} q Rotation quaternion
- * @param {vec3} v Translation vector
- * @param {vec3} s Scaling vector
- * @param {vec3} o The origin vector around which to scale and rotate
- * @returns {mat4} out
- */
-function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
-  // Quaternion math
-  let x = q[0], y = q[1], z = q[2], w = q[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-
-  let xx = x * x2;
-  let xy = x * y2;
-  let xz = x * z2;
-  let yy = y * y2;
-  let yz = y * z2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-
-  let sx = s[0];
-  let sy = s[1];
-  let sz = s[2];
-
-  let ox = o[0];
-  let oy = o[1];
-  let oz = o[2];
-
-  let out0 = (1 - (yy + zz)) * sx;
-  let out1 = (xy + wz) * sx;
-  let out2 = (xz - wy) * sx;
-  let out4 = (xy - wz) * sy;
-  let out5 = (1 - (xx + zz)) * sy;
-  let out6 = (yz + wx) * sy;
-  let out8 = (xz + wy) * sz;
-  let out9 = (yz - wx) * sz;
-  let out10 = (1 - (xx + yy)) * sz;
-
-  out[0] = out0;
-  out[1] = out1;
-  out[2] = out2;
-  out[3] = 0;
-  out[4] = out4;
-  out[5] = out5;
-  out[6] = out6;
-  out[7] = 0;
-  out[8] = out8;
-  out[9] = out9;
-  out[10] = out10;
-  out[11] = 0;
-  out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
-  out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
-  out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
-  out[15] = 1;
-
-  return out;
-}
-
-/**
- * Calculates a 4x4 matrix from the given quaternion
- *
- * @param {mat4} out mat4 receiving operation result
- * @param {quat} q Quaternion to create matrix from
- *
- * @returns {mat4} out
- */
-function fromQuat(out, q) {
-  let x = q[0], y = q[1], z = q[2], w = q[3];
-  let x2 = x + x;
-  let y2 = y + y;
-  let z2 = z + z;
-
-  let xx = x * x2;
-  let yx = y * x2;
-  let yy = y * y2;
-  let zx = z * x2;
-  let zy = z * y2;
-  let zz = z * z2;
-  let wx = w * x2;
-  let wy = w * y2;
-  let wz = w * z2;
-
-  out[0] = 1 - yy - zz;
-  out[1] = yx + wz;
-  out[2] = zx - wy;
-  out[3] = 0;
-
-  out[4] = yx - wz;
-  out[5] = 1 - xx - zz;
-  out[6] = zy + wx;
-  out[7] = 0;
-
-  out[8] = zx + wy;
-  out[9] = zy - wx;
-  out[10] = 1 - xx - yy;
-  out[11] = 0;
-
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = 0;
-  out[15] = 1;
-
-  return out;
-}
-
-/**
- * Generates a frustum matrix with the given bounds
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {Number} left Left bound of the frustum
- * @param {Number} right Right bound of the frustum
- * @param {Number} bottom Bottom bound of the frustum
- * @param {Number} top Top bound of the frustum
- * @param {Number} near Near bound of the frustum
- * @param {Number} far Far bound of the frustum
- * @returns {mat4} out
- */
-function frustum(out, left, right, bottom, top, near, far) {
-  let rl = 1 / (right - left);
-  let tb = 1 / (top - bottom);
-  let nf = 1 / (near - far);
-  out[0] = (near * 2) * rl;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = (near * 2) * tb;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = (right + left) * rl;
-  out[9] = (top + bottom) * tb;
-  out[10] = (far + near) * nf;
-  out[11] = -1;
-  out[12] = 0;
-  out[13] = 0;
-  out[14] = (far * near * 2) * nf;
-  out[15] = 0;
-  return out;
-}
-
-/**
- * Generates a perspective projection matrix with the given bounds.
- * Passing null/undefined/no value for far will generate infinite projection matrix.
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {number} fovy Vertical field of view in radians
- * @param {number} aspect Aspect ratio. typically viewport width/height
- * @param {number} near Near bound of the frustum
- * @param {number} far Far bound of the frustum, can be null or Infinity
- * @returns {mat4} out
- */
-function perspective(out, fovy, aspect, near, far) {
-  let f = 1.0 / Math.tan(fovy / 2), nf;
-  out[0] = f / aspect;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = f;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[11] = -1;
-  out[12] = 0;
-  out[13] = 0;
-  out[15] = 0;
-  if (far != null && far !== Infinity) {
-    nf = 1 / (near - far);
-    out[10] = (far + near) * nf;
-    out[14] = (2 * far * near) * nf;
-  } else {
-    out[10] = -1;
-    out[14] = -2 * near;
-  }
-  return out;
-}
-
-/**
- * Generates a perspective projection matrix with the given field of view.
- * This is primarily useful for generating projection matrices to be used
- * with the still experiemental WebVR API.
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
- * @param {number} near Near bound of the frustum
- * @param {number} far Far bound of the frustum
- * @returns {mat4} out
- */
-function perspectiveFromFieldOfView(out, fov, near, far) {
-  let upTan = Math.tan(fov.upDegrees * Math.PI/180.0);
-  let downTan = Math.tan(fov.downDegrees * Math.PI/180.0);
-  let leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0);
-  let rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0);
-  let xScale = 2.0 / (leftTan + rightTan);
-  let yScale = 2.0 / (upTan + downTan);
-
-  out[0] = xScale;
-  out[1] = 0.0;
-  out[2] = 0.0;
-  out[3] = 0.0;
-  out[4] = 0.0;
-  out[5] = yScale;
-  out[6] = 0.0;
-  out[7] = 0.0;
-  out[8] = -((leftTan - rightTan) * xScale * 0.5);
-  out[9] = ((upTan - downTan) * yScale * 0.5);
-  out[10] = far / (near - far);
-  out[11] = -1.0;
-  out[12] = 0.0;
-  out[13] = 0.0;
-  out[14] = (far * near) / (near - far);
-  out[15] = 0.0;
-  return out;
-}
-
-/**
- * Generates a orthogonal projection matrix with the given bounds
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {number} left Left bound of the frustum
- * @param {number} right Right bound of the frustum
- * @param {number} bottom Bottom bound of the frustum
- * @param {number} top Top bound of the frustum
- * @param {number} near Near bound of the frustum
- * @param {number} far Far bound of the frustum
- * @returns {mat4} out
- */
-function ortho(out, left, right, bottom, top, near, far) {
-  let lr = 1 / (left - right);
-  let bt = 1 / (bottom - top);
-  let nf = 1 / (near - far);
-  out[0] = -2 * lr;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 0;
-  out[4] = 0;
-  out[5] = -2 * bt;
-  out[6] = 0;
-  out[7] = 0;
-  out[8] = 0;
-  out[9] = 0;
-  out[10] = 2 * nf;
-  out[11] = 0;
-  out[12] = (left + right) * lr;
-  out[13] = (top + bottom) * bt;
-  out[14] = (far + near) * nf;
-  out[15] = 1;
-  return out;
-}
-
-/**
- * Generates a look-at matrix with the given eye position, focal point, and up axis.
- * If you want a matrix that actually makes an object look at another object, you should use targetTo instead.
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {vec3} eye Position of the viewer
- * @param {vec3} center Point the viewer is looking at
- * @param {vec3} up vec3 pointing up
- * @returns {mat4} out
- */
-function lookAt(out, eye, center, up) {
-  let x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
-  let eyex = eye[0];
-  let eyey = eye[1];
-  let eyez = eye[2];
-  let upx = up[0];
-  let upy = up[1];
-  let upz = up[2];
-  let centerx = center[0];
-  let centery = center[1];
-  let centerz = center[2];
-
-  if (Math.abs(eyex - centerx) < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] &&
-      Math.abs(eyey - centery) < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] &&
-      Math.abs(eyez - centerz) < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]) {
-    return identity(out);
-  }
-
-  z0 = eyex - centerx;
-  z1 = eyey - centery;
-  z2 = eyez - centerz;
-
-  len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
-  z0 *= len;
-  z1 *= len;
-  z2 *= len;
-
-  x0 = upy * z2 - upz * z1;
-  x1 = upz * z0 - upx * z2;
-  x2 = upx * z1 - upy * z0;
-  len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
-  if (!len) {
-    x0 = 0;
-    x1 = 0;
-    x2 = 0;
-  } else {
-    len = 1 / len;
-    x0 *= len;
-    x1 *= len;
-    x2 *= len;
-  }
-
-  y0 = z1 * x2 - z2 * x1;
-  y1 = z2 * x0 - z0 * x2;
-  y2 = z0 * x1 - z1 * x0;
-
-  len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
-  if (!len) {
-    y0 = 0;
-    y1 = 0;
-    y2 = 0;
-  } else {
-    len = 1 / len;
-    y0 *= len;
-    y1 *= len;
-    y2 *= len;
-  }
-
-  out[0] = x0;
-  out[1] = y0;
-  out[2] = z0;
-  out[3] = 0;
-  out[4] = x1;
-  out[5] = y1;
-  out[6] = z1;
-  out[7] = 0;
-  out[8] = x2;
-  out[9] = y2;
-  out[10] = z2;
-  out[11] = 0;
-  out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
-  out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
-  out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
-  out[15] = 1;
-
-  return out;
-}
-
-/**
- * Generates a matrix that makes something look at something else.
- *
- * @param {mat4} out mat4 frustum matrix will be written into
- * @param {vec3} eye Position of the viewer
- * @param {vec3} center Point the viewer is looking at
- * @param {vec3} up vec3 pointing up
- * @returns {mat4} out
- */
-function targetTo(out, eye, target, up) {
-  let eyex = eye[0],
-      eyey = eye[1],
-      eyez = eye[2],
-      upx = up[0],
-      upy = up[1],
-      upz = up[2];
-
-  let z0 = eyex - target[0],
-      z1 = eyey - target[1],
-      z2 = eyez - target[2];
-
-  let len = z0*z0 + z1*z1 + z2*z2;
-  if (len > 0) {
-    len = 1 / Math.sqrt(len);
-    z0 *= len;
-    z1 *= len;
-    z2 *= len;
-  }
-
-  let x0 = upy * z2 - upz * z1,
-      x1 = upz * z0 - upx * z2,
-      x2 = upx * z1 - upy * z0;
-
-  len = x0*x0 + x1*x1 + x2*x2;
-  if (len > 0) {
-    len = 1 / Math.sqrt(len);
-    x0 *= len;
-    x1 *= len;
-    x2 *= len;
-  }
-
-  out[0] = x0;
-  out[1] = x1;
-  out[2] = x2;
-  out[3] = 0;
-  out[4] = z1 * x2 - z2 * x1;
-  out[5] = z2 * x0 - z0 * x2;
-  out[6] = z0 * x1 - z1 * x0;
-  out[7] = 0;
-  out[8] = z0;
-  out[9] = z1;
-  out[10] = z2;
-  out[11] = 0;
-  out[12] = eyex;
-  out[13] = eyey;
-  out[14] = eyez;
-  out[15] = 1;
-  return out;
-};
-
-/**
- * Returns a string representation of a mat4
- *
- * @param {mat4} a matrix to represent as a string
- * @returns {String} string representation of the matrix
- */
-function str(a) {
-  return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
-          a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
-          a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
-          a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
-}
-
-/**
- * Returns Frobenius norm of a mat4
- *
- * @param {mat4} a the matrix to calculate Frobenius norm of
- * @returns {Number} Frobenius norm
- */
-function frob(a) {
-  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2) + Math.pow(a[9], 2) + Math.pow(a[10], 2) + Math.pow(a[11], 2) + Math.pow(a[12], 2) + Math.pow(a[13], 2) + Math.pow(a[14], 2) + Math.pow(a[15], 2) ))
-}
-
-/**
- * Adds two mat4's
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the first operand
- * @param {mat4} b the second operand
- * @returns {mat4} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  out[4] = a[4] + b[4];
-  out[5] = a[5] + b[5];
-  out[6] = a[6] + b[6];
-  out[7] = a[7] + b[7];
-  out[8] = a[8] + b[8];
-  out[9] = a[9] + b[9];
-  out[10] = a[10] + b[10];
-  out[11] = a[11] + b[11];
-  out[12] = a[12] + b[12];
-  out[13] = a[13] + b[13];
-  out[14] = a[14] + b[14];
-  out[15] = a[15] + b[15];
-  return out;
-}
-
-/**
- * Subtracts matrix b from matrix a
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the first operand
- * @param {mat4} b the second operand
- * @returns {mat4} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  out[3] = a[3] - b[3];
-  out[4] = a[4] - b[4];
-  out[5] = a[5] - b[5];
-  out[6] = a[6] - b[6];
-  out[7] = a[7] - b[7];
-  out[8] = a[8] - b[8];
-  out[9] = a[9] - b[9];
-  out[10] = a[10] - b[10];
-  out[11] = a[11] - b[11];
-  out[12] = a[12] - b[12];
-  out[13] = a[13] - b[13];
-  out[14] = a[14] - b[14];
-  out[15] = a[15] - b[15];
-  return out;
-}
-
-/**
- * Multiply each element of the matrix by a scalar.
- *
- * @param {mat4} out the receiving matrix
- * @param {mat4} a the matrix to scale
- * @param {Number} b amount to scale the matrix's elements by
- * @returns {mat4} out
- */
-function multiplyScalar(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  out[4] = a[4] * b;
-  out[5] = a[5] * b;
-  out[6] = a[6] * b;
-  out[7] = a[7] * b;
-  out[8] = a[8] * b;
-  out[9] = a[9] * b;
-  out[10] = a[10] * b;
-  out[11] = a[11] * b;
-  out[12] = a[12] * b;
-  out[13] = a[13] * b;
-  out[14] = a[14] * b;
-  out[15] = a[15] * b;
-  return out;
-}
-
-/**
- * Adds two mat4's after multiplying each element of the second operand by a scalar value.
- *
- * @param {mat4} out the receiving vector
- * @param {mat4} a the first operand
- * @param {mat4} b the second operand
- * @param {Number} scale the amount to scale b's elements by before adding
- * @returns {mat4} out
- */
-function multiplyScalarAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  out[3] = a[3] + (b[3] * scale);
-  out[4] = a[4] + (b[4] * scale);
-  out[5] = a[5] + (b[5] * scale);
-  out[6] = a[6] + (b[6] * scale);
-  out[7] = a[7] + (b[7] * scale);
-  out[8] = a[8] + (b[8] * scale);
-  out[9] = a[9] + (b[9] * scale);
-  out[10] = a[10] + (b[10] * scale);
-  out[11] = a[11] + (b[11] * scale);
-  out[12] = a[12] + (b[12] * scale);
-  out[13] = a[13] + (b[13] * scale);
-  out[14] = a[14] + (b[14] * scale);
-  out[15] = a[15] + (b[15] * scale);
-  return out;
-}
-
-/**
- * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
- *
- * @param {mat4} a The first matrix.
- * @param {mat4} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&
-         a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] &&
-         a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] &&
-         a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];
-}
-
-/**
- * Returns whether or not the matrices have approximately the same elements in the same position.
- *
- * @param {mat4} a The first matrix.
- * @param {mat4} b The second matrix.
- * @returns {Boolean} True if the matrices are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0  = a[0],  a1  = a[1],  a2  = a[2],  a3  = a[3];
-  let a4  = a[4],  a5  = a[5],  a6  = a[6],  a7  = a[7];
-  let a8  = a[8],  a9  = a[9],  a10 = a[10], a11 = a[11];
-  let a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];
-
-  let b0  = b[0],  b1  = b[1],  b2  = b[2],  b3  = b[3];
-  let b4  = b[4],  b5  = b[5],  b6  = b[6],  b7  = b[7];
-  let b8  = b[8],  b9  = b[9],  b10 = b[10], b11 = b[11];
-  let b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
-
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-          Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
-          Math.abs(a4 - b4) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
-          Math.abs(a5 - b5) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
-          Math.abs(a6 - b6) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
-          Math.abs(a7 - b7) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
-          Math.abs(a8 - b8) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a8), Math.abs(b8)) &&
-          Math.abs(a9 - b9) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a9), Math.abs(b9)) &&
-          Math.abs(a10 - b10) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a10), Math.abs(b10)) &&
-          Math.abs(a11 - b11) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a11), Math.abs(b11)) &&
-          Math.abs(a12 - b12) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a12), Math.abs(b12)) &&
-          Math.abs(a13 - b13) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a13), Math.abs(b13)) &&
-          Math.abs(a14 - b14) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a14), Math.abs(b14)) &&
-          Math.abs(a15 - b15) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a15), Math.abs(b15)));
-}
-
-/**
- * Alias for {@link mat4.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link mat4.subtract}
- * @function
- */
-const sub = subtract;
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/quat.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/quat.js ***!
-  \******************************************************/
-/*! exports provided: create, identity, setAxisAngle, getAxisAngle, multiply, rotateX, rotateY, rotateZ, calculateW, slerp, random, invert, conjugate, fromMat3, fromEuler, str, clone, fromValues, copy, set, add, mul, scale, dot, lerp, length, len, squaredLength, sqrLen, normalize, exactEquals, equals, rotationTo, sqlerp, setAxes */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setAxisAngle", function() { return setAxisAngle; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAxisAngle", function() { return getAxisAngle; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateX", function() { return rotateX; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateY", function() { return rotateY; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateZ", function() { return rotateZ; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "calculateW", function() { return calculateW; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "slerp", function() { return slerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "random", function() { return random; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "conjugate", function() { return conjugate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromMat3", function() { return fromMat3; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromEuler", function() { return fromEuler; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dot", function() { return dot; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "length", function() { return length; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "len", function() { return len; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredLength", function() { return squaredLength; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrLen", function() { return sqrLen; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalize", function() { return normalize; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotationTo", function() { return rotationTo; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqlerp", function() { return sqlerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setAxes", function() { return setAxes; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-/* harmony import */ var _mat3_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./mat3.js */ "./node_modules/gl-matrix/src/gl-matrix/mat3.js");
-/* harmony import */ var _vec3_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./vec3.js */ "./node_modules/gl-matrix/src/gl-matrix/vec3.js");
-/* harmony import */ var _vec4_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./vec4.js */ "./node_modules/gl-matrix/src/gl-matrix/vec4.js");
-
-
-
-
-
-/**
- * Quaternion
- * @module quat
- */
-
-/**
- * Creates a new identity quat
- *
- * @returns {quat} a new quaternion
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[0] = 0;
-    out[1] = 0;
-    out[2] = 0;
-  }
-  out[3] = 1;
-  return out;
-}
-
-/**
- * Set a quat to the identity quaternion
- *
- * @param {quat} out the receiving quaternion
- * @returns {quat} out
- */
-function identity(out) {
-  out[0] = 0;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  return out;
-}
-
-/**
- * Sets a quat from the given angle and rotation axis,
- * then returns it.
- *
- * @param {quat} out the receiving quaternion
- * @param {vec3} axis the axis around which to rotate
- * @param {Number} rad the angle in radians
- * @returns {quat} out
- **/
-function setAxisAngle(out, axis, rad) {
-  rad = rad * 0.5;
-  let s = Math.sin(rad);
-  out[0] = s * axis[0];
-  out[1] = s * axis[1];
-  out[2] = s * axis[2];
-  out[3] = Math.cos(rad);
-  return out;
-}
-
-/**
- * Gets the rotation axis and angle for a given
- *  quaternion. If a quaternion is created with
- *  setAxisAngle, this method will return the same
- *  values as providied in the original parameter list
- *  OR functionally equivalent values.
- * Example: The quaternion formed by axis [0, 0, 1] and
- *  angle -90 is the same as the quaternion formed by
- *  [0, 0, 1] and 270. This method favors the latter.
- * @param  {vec3} out_axis  Vector receiving the axis of rotation
- * @param  {quat} q     Quaternion to be decomposed
- * @return {Number}     Angle, in radians, of the rotation
- */
-function getAxisAngle(out_axis, q) {
-  let rad = Math.acos(q[3]) * 2.0;
-  let s = Math.sin(rad / 2.0);
-  if (s > _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]) {
-    out_axis[0] = q[0] / s;
-    out_axis[1] = q[1] / s;
-    out_axis[2] = q[2] / s;
-  } else {
-    // If s is zero, return any axis (no rotation - axis does not matter)
-    out_axis[0] = 1;
-    out_axis[1] = 0;
-    out_axis[2] = 0;
-  }
-  return rad;
-}
-
-/**
- * Multiplies two quat's
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @returns {quat} out
- */
-function multiply(out, a, b) {
-  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
-  let bx = b[0], by = b[1], bz = b[2], bw = b[3];
-
-  out[0] = ax * bw + aw * bx + ay * bz - az * by;
-  out[1] = ay * bw + aw * by + az * bx - ax * bz;
-  out[2] = az * bw + aw * bz + ax * by - ay * bx;
-  out[3] = aw * bw - ax * bx - ay * by - az * bz;
-  return out;
-}
-
-/**
- * Rotates a quaternion by the given angle about the X axis
- *
- * @param {quat} out quat receiving operation result
- * @param {quat} a quat to rotate
- * @param {number} rad angle (in radians) to rotate
- * @returns {quat} out
- */
-function rotateX(out, a, rad) {
-  rad *= 0.5;
-
-  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
-  let bx = Math.sin(rad), bw = Math.cos(rad);
-
-  out[0] = ax * bw + aw * bx;
-  out[1] = ay * bw + az * bx;
-  out[2] = az * bw - ay * bx;
-  out[3] = aw * bw - ax * bx;
-  return out;
-}
-
-/**
- * Rotates a quaternion by the given angle about the Y axis
- *
- * @param {quat} out quat receiving operation result
- * @param {quat} a quat to rotate
- * @param {number} rad angle (in radians) to rotate
- * @returns {quat} out
- */
-function rotateY(out, a, rad) {
-  rad *= 0.5;
-
-  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
-  let by = Math.sin(rad), bw = Math.cos(rad);
-
-  out[0] = ax * bw - az * by;
-  out[1] = ay * bw + aw * by;
-  out[2] = az * bw + ax * by;
-  out[3] = aw * bw - ay * by;
-  return out;
-}
-
-/**
- * Rotates a quaternion by the given angle about the Z axis
- *
- * @param {quat} out quat receiving operation result
- * @param {quat} a quat to rotate
- * @param {number} rad angle (in radians) to rotate
- * @returns {quat} out
- */
-function rotateZ(out, a, rad) {
-  rad *= 0.5;
-
-  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
-  let bz = Math.sin(rad), bw = Math.cos(rad);
-
-  out[0] = ax * bw + ay * bz;
-  out[1] = ay * bw - ax * bz;
-  out[2] = az * bw + aw * bz;
-  out[3] = aw * bw - az * bz;
-  return out;
-}
-
-/**
- * Calculates the W component of a quat from the X, Y, and Z components.
- * Assumes that quaternion is 1 unit in length.
- * Any existing W component will be ignored.
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a quat to calculate W component of
- * @returns {quat} out
- */
-function calculateW(out, a) {
-  let x = a[0], y = a[1], z = a[2];
-
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-  out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
-  return out;
-}
-
-/**
- * Performs a spherical linear interpolation between two quat
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {quat} out
- */
-function slerp(out, a, b, t) {
-  // benchmarks:
-  //    http://jsperf.com/quaternion-slerp-implementations
-  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
-  let bx = b[0], by = b[1], bz = b[2], bw = b[3];
-
-  let omega, cosom, sinom, scale0, scale1;
-
-  // calc cosine
-  cosom = ax * bx + ay * by + az * bz + aw * bw;
-  // adjust signs (if necessary)
-  if ( cosom < 0.0 ) {
-    cosom = -cosom;
-    bx = - bx;
-    by = - by;
-    bz = - bz;
-    bw = - bw;
-  }
-  // calculate coefficients
-  if ( (1.0 - cosom) > _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] ) {
-    // standard case (slerp)
-    omega  = Math.acos(cosom);
-    sinom  = Math.sin(omega);
-    scale0 = Math.sin((1.0 - t) * omega) / sinom;
-    scale1 = Math.sin(t * omega) / sinom;
-  } else {
-    // "from" and "to" quaternions are very close
-    //  ... so we can do a linear interpolation
-    scale0 = 1.0 - t;
-    scale1 = t;
-  }
-  // calculate final values
-  out[0] = scale0 * ax + scale1 * bx;
-  out[1] = scale0 * ay + scale1 * by;
-  out[2] = scale0 * az + scale1 * bz;
-  out[3] = scale0 * aw + scale1 * bw;
-
-  return out;
-}
-
-/**
- * Generates a random quaternion
- *
- * @param {quat} out the receiving quaternion
- * @returns {quat} out
- */
-function random(out) {
-  // Implementation of http://planning.cs.uiuc.edu/node198.html
-  // TODO: Calling random 3 times is probably not the fastest solution
-  let u1 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]();
-  let u2 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]();
-  let u3 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]();
-
-  let sqrt1MinusU1 = Math.sqrt(1 - u1);
-  let sqrtU1 = Math.sqrt(u1);
-
-  out[0] = sqrt1MinusU1 * Math.sin(2.0 * Math.PI * u2);
-  out[1] = sqrt1MinusU1 * Math.cos(2.0 * Math.PI * u2);
-  out[2] = sqrtU1 * Math.sin(2.0 * Math.PI * u3);
-  out[3] = sqrtU1 * Math.cos(2.0 * Math.PI * u3);
-  return out;
-}
-
-/**
- * Calculates the inverse of a quat
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a quat to calculate inverse of
- * @returns {quat} out
- */
-function invert(out, a) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let dot = a0*a0 + a1*a1 + a2*a2 + a3*a3;
-  let invDot = dot ? 1.0/dot : 0;
-
-  // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
-
-  out[0] = -a0*invDot;
-  out[1] = -a1*invDot;
-  out[2] = -a2*invDot;
-  out[3] = a3*invDot;
-  return out;
-}
-
-/**
- * Calculates the conjugate of a quat
- * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a quat to calculate conjugate of
- * @returns {quat} out
- */
-function conjugate(out, a) {
-  out[0] = -a[0];
-  out[1] = -a[1];
-  out[2] = -a[2];
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Creates a quaternion from the given 3x3 rotation matrix.
- *
- * NOTE: The resultant quaternion is not normalized, so you should be sure
- * to renormalize the quaternion yourself where necessary.
- *
- * @param {quat} out the receiving quaternion
- * @param {mat3} m rotation matrix
- * @returns {quat} out
- * @function
- */
-function fromMat3(out, m) {
-  // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
-  // article "Quaternion Calculus and Fast Animation".
-  let fTrace = m[0] + m[4] + m[8];
-  let fRoot;
-
-  if ( fTrace > 0.0 ) {
-    // |w| > 1/2, may as well choose w > 1/2
-    fRoot = Math.sqrt(fTrace + 1.0);  // 2w
-    out[3] = 0.5 * fRoot;
-    fRoot = 0.5/fRoot;  // 1/(4w)
-    out[0] = (m[5]-m[7])*fRoot;
-    out[1] = (m[6]-m[2])*fRoot;
-    out[2] = (m[1]-m[3])*fRoot;
-  } else {
-    // |w| <= 1/2
-    let i = 0;
-    if ( m[4] > m[0] )
-      i = 1;
-    if ( m[8] > m[i*3+i] )
-      i = 2;
-    let j = (i+1)%3;
-    let k = (i+2)%3;
-
-    fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0);
-    out[i] = 0.5 * fRoot;
-    fRoot = 0.5 / fRoot;
-    out[3] = (m[j*3+k] - m[k*3+j]) * fRoot;
-    out[j] = (m[j*3+i] + m[i*3+j]) * fRoot;
-    out[k] = (m[k*3+i] + m[i*3+k]) * fRoot;
-  }
-
-  return out;
-}
-
-/**
- * Creates a quaternion from the given euler angle x, y, z.
- *
- * @param {quat} out the receiving quaternion
- * @param {x} Angle to rotate around X axis in degrees.
- * @param {y} Angle to rotate around Y axis in degrees.
- * @param {z} Angle to rotate around Z axis in degrees.
- * @returns {quat} out
- * @function
- */
-function fromEuler(out, x, y, z) {
-    let halfToRad = 0.5 * Math.PI / 180.0;
-    x *= halfToRad;
-    y *= halfToRad;
-    z *= halfToRad;
-
-    let sx = Math.sin(x);
-    let cx = Math.cos(x);
-    let sy = Math.sin(y);
-    let cy = Math.cos(y);
-    let sz = Math.sin(z);
-    let cz = Math.cos(z);
-
-    out[0] = sx * cy * cz - cx * sy * sz;
-    out[1] = cx * sy * cz + sx * cy * sz;
-    out[2] = cx * cy * sz - sx * sy * cz;
-    out[3] = cx * cy * cz + sx * sy * sz;
-
-    return out;
-}
-
-/**
- * Returns a string representation of a quatenion
- *
- * @param {quat} a vector to represent as a string
- * @returns {String} string representation of the vector
- */
-function str(a) {
-  return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
-}
-
-/**
- * Creates a new quat initialized with values from an existing quaternion
- *
- * @param {quat} a quaternion to clone
- * @returns {quat} a new quaternion
- * @function
- */
-const clone = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["clone"];
-
-/**
- * Creates a new quat initialized with the given values
- *
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @param {Number} w W component
- * @returns {quat} a new quaternion
- * @function
- */
-const fromValues = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["fromValues"];
-
-/**
- * Copy the values from one quat to another
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the source quaternion
- * @returns {quat} out
- * @function
- */
-const copy = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["copy"];
-
-/**
- * Set the components of a quat to the given values
- *
- * @param {quat} out the receiving quaternion
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @param {Number} w W component
- * @returns {quat} out
- * @function
- */
-const set = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["set"];
-
-/**
- * Adds two quat's
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @returns {quat} out
- * @function
- */
-const add = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["add"];
-
-/**
- * Alias for {@link quat.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Scales a quat by a scalar number
- *
- * @param {quat} out the receiving vector
- * @param {quat} a the vector to scale
- * @param {Number} b amount to scale the vector by
- * @returns {quat} out
- * @function
- */
-const scale = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["scale"];
-
-/**
- * Calculates the dot product of two quat's
- *
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @returns {Number} dot product of a and b
- * @function
- */
-const dot = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["dot"];
-
-/**
- * Performs a linear interpolation between two quat's
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {quat} out
- * @function
- */
-const lerp = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["lerp"];
-
-/**
- * Calculates the length of a quat
- *
- * @param {quat} a vector to calculate length of
- * @returns {Number} length of a
- */
-const length = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["length"];
-
-/**
- * Alias for {@link quat.length}
- * @function
- */
-const len = length;
-
-/**
- * Calculates the squared length of a quat
- *
- * @param {quat} a vector to calculate squared length of
- * @returns {Number} squared length of a
- * @function
- */
-const squaredLength = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["squaredLength"];
-
-/**
- * Alias for {@link quat.squaredLength}
- * @function
- */
-const sqrLen = squaredLength;
-
-/**
- * Normalize a quat
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a quaternion to normalize
- * @returns {quat} out
- * @function
- */
-const normalize = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["normalize"];
-
-/**
- * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===)
- *
- * @param {quat} a The first quaternion.
- * @param {quat} b The second quaternion.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-const exactEquals = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["exactEquals"];
-
-/**
- * Returns whether or not the quaternions have approximately the same elements in the same position.
- *
- * @param {quat} a The first vector.
- * @param {quat} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-const equals = _vec4_js__WEBPACK_IMPORTED_MODULE_3__["equals"];
-
-/**
- * Sets a quaternion to represent the shortest rotation from one
- * vector to another.
- *
- * Both vectors are assumed to be unit length.
- *
- * @param {quat} out the receiving quaternion.
- * @param {vec3} a the initial vector
- * @param {vec3} b the destination vector
- * @returns {quat} out
- */
-const rotationTo = (function() {
-  let tmpvec3 = _vec3_js__WEBPACK_IMPORTED_MODULE_2__["create"]();
-  let xUnitVec3 = _vec3_js__WEBPACK_IMPORTED_MODULE_2__["fromValues"](1,0,0);
-  let yUnitVec3 = _vec3_js__WEBPACK_IMPORTED_MODULE_2__["fromValues"](0,1,0);
-
-  return function(out, a, b) {
-    let dot = _vec3_js__WEBPACK_IMPORTED_MODULE_2__["dot"](a, b);
-    if (dot < -0.999999) {
-      _vec3_js__WEBPACK_IMPORTED_MODULE_2__["cross"](tmpvec3, xUnitVec3, a);
-      if (_vec3_js__WEBPACK_IMPORTED_MODULE_2__["len"](tmpvec3) < 0.000001)
-        _vec3_js__WEBPACK_IMPORTED_MODULE_2__["cross"](tmpvec3, yUnitVec3, a);
-      _vec3_js__WEBPACK_IMPORTED_MODULE_2__["normalize"](tmpvec3, tmpvec3);
-      setAxisAngle(out, tmpvec3, Math.PI);
-      return out;
-    } else if (dot > 0.999999) {
-      out[0] = 0;
-      out[1] = 0;
-      out[2] = 0;
-      out[3] = 1;
-      return out;
-    } else {
-      _vec3_js__WEBPACK_IMPORTED_MODULE_2__["cross"](tmpvec3, a, b);
-      out[0] = tmpvec3[0];
-      out[1] = tmpvec3[1];
-      out[2] = tmpvec3[2];
-      out[3] = 1 + dot;
-      return normalize(out, out);
-    }
-  };
-})();
-
-/**
- * Performs a spherical linear interpolation with two control points
- *
- * @param {quat} out the receiving quaternion
- * @param {quat} a the first operand
- * @param {quat} b the second operand
- * @param {quat} c the third operand
- * @param {quat} d the fourth operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {quat} out
- */
-const sqlerp = (function () {
-  let temp1 = create();
-  let temp2 = create();
-
-  return function (out, a, b, c, d, t) {
-    slerp(temp1, a, d, t);
-    slerp(temp2, b, c, t);
-    slerp(out, temp1, temp2, 2 * t * (1 - t));
-
-    return out;
-  };
-}());
-
-/**
- * Sets the specified quaternion with values corresponding to the given
- * axes. Each axis is a vec3 and is expected to be unit length and
- * perpendicular to all other specified axes.
- *
- * @param {vec3} view  the vector representing the viewing direction
- * @param {vec3} right the vector representing the local "right" direction
- * @param {vec3} up    the vector representing the local "up" direction
- * @returns {quat} out
- */
-const setAxes = (function() {
-  let matr = _mat3_js__WEBPACK_IMPORTED_MODULE_1__["create"]();
-
-  return function(out, view, right, up) {
-    matr[0] = right[0];
-    matr[3] = right[1];
-    matr[6] = right[2];
-
-    matr[1] = up[0];
-    matr[4] = up[1];
-    matr[7] = up[2];
-
-    matr[2] = -view[0];
-    matr[5] = -view[1];
-    matr[8] = -view[2];
-
-    return normalize(out, fromMat3(out, matr));
-  };
-})();
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/quat2.js":
-/*!*******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/quat2.js ***!
-  \*******************************************************/
-/*! exports provided: create, clone, fromValues, fromRotationTranslationValues, fromRotationTranslation, fromTranslation, fromRotation, fromMat4, copy, identity, set, getReal, getDual, setReal, setDual, getTranslation, translate, rotateX, rotateY, rotateZ, rotateByQuatAppend, rotateByQuatPrepend, rotateAroundAxis, add, multiply, mul, scale, dot, lerp, invert, conjugate, length, len, squaredLength, sqrLen, normalize, str, exactEquals, equals */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotationTranslationValues", function() { return fromRotationTranslationValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotationTranslation", function() { return fromRotationTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromTranslation", function() { return fromTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromRotation", function() { return fromRotation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromMat4", function() { return fromMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getReal", function() { return getReal; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDual", function() { return getDual; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setReal", function() { return setReal; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setDual", function() { return setDual; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getTranslation", function() { return getTranslation; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "translate", function() { return translate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateX", function() { return rotateX; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateY", function() { return rotateY; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateZ", function() { return rotateZ; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateByQuatAppend", function() { return rotateByQuatAppend; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateByQuatPrepend", function() { return rotateByQuatPrepend; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateAroundAxis", function() { return rotateAroundAxis; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dot", function() { return dot; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "invert", function() { return invert; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "conjugate", function() { return conjugate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "length", function() { return length; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "len", function() { return len; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredLength", function() { return squaredLength; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrLen", function() { return sqrLen; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalize", function() { return normalize; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-/* harmony import */ var _quat_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quat.js */ "./node_modules/gl-matrix/src/gl-matrix/quat.js");
-/* harmony import */ var _mat4_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./mat4.js */ "./node_modules/gl-matrix/src/gl-matrix/mat4.js");
-
-
-
-
-/**
- * Dual Quaternion<br>
- * Format: [real, dual]<br>
- * Quaternion format: XYZW<br>
- * Make sure to have normalized dual quaternions, otherwise the functions may not work as intended.<br>
- * @module quat2
- */
-
-
-/**
- * Creates a new identity dual quat
- *
- * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation]
- */
-function create() {
-  let dq = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](8);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    dq[0] = 0;
-    dq[1] = 0;
-    dq[2] = 0;
-    dq[4] = 0;
-    dq[5] = 0;
-    dq[6] = 0;
-    dq[7] = 0;
-  }
-  dq[3] = 1;
-  return dq;
-}
-
-/**
- * Creates a new quat initialized with values from an existing quaternion
- *
- * @param {quat2} a dual quaternion to clone
- * @returns {quat2} new dual quaternion
- * @function
- */
-function clone(a) {
-  let dq = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](8);
-  dq[0] = a[0];
-  dq[1] = a[1];
-  dq[2] = a[2];
-  dq[3] = a[3];
-  dq[4] = a[4];
-  dq[5] = a[5];
-  dq[6] = a[6];
-  dq[7] = a[7];
-  return dq;
-}
-
-/**
- * Creates a new dual quat initialized with the given values
- *
- * @param {Number} x1 X component
- * @param {Number} y1 Y component
- * @param {Number} z1 Z component
- * @param {Number} w1 W component
- * @param {Number} x2 X component
- * @param {Number} y2 Y component
- * @param {Number} z2 Z component
- * @param {Number} w2 W component
- * @returns {quat2} new dual quaternion
- * @function
- */
-function fromValues(x1, y1, z1, w1, x2, y2, z2, w2) {
-  let dq = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](8);
-  dq[0] = x1;
-  dq[1] = y1;
-  dq[2] = z1;
-  dq[3] = w1;
-  dq[4] = x2;
-  dq[5] = y2;
-  dq[6] = z2;
-  dq[7] = w2;
-  return dq;
-}
-
-/**
- * Creates a new dual quat from the given values (quat and translation)
- *
- * @param {Number} x1 X component
- * @param {Number} y1 Y component
- * @param {Number} z1 Z component
- * @param {Number} w1 W component
- * @param {Number} x2 X component (translation)
- * @param {Number} y2 Y component (translation)
- * @param {Number} z2 Z component (translation)
- * @returns {quat2} new dual quaternion
- * @function
- */
-function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) {
-  let dq = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](8);
-  dq[0] = x1;
-  dq[1] = y1;
-  dq[2] = z1;
-  dq[3] = w1;
-  let ax = x2 * 0.5,
-    ay = y2 * 0.5,
-    az = z2 * 0.5;
-  dq[4] = ax * w1 + ay * z1 - az * y1;
-  dq[5] = ay * w1 + az * x1 - ax * z1;
-  dq[6] = az * w1 + ax * y1 - ay * x1;
-  dq[7] = -ax * x1 - ay * y1 - az * z1;
-  return dq;
-}
-
-/**
- * Creates a dual quat from a quaternion and a translation
- *
- * @param {quat2} dual quaternion receiving operation result
- * @param {quat} q quaternion
- * @param {vec3} t tranlation vector
- * @returns {quat2} dual quaternion receiving operation result
- * @function
- */
-function fromRotationTranslation(out, q, t) {
-  let ax = t[0] * 0.5,
-    ay = t[1] * 0.5,
-    az = t[2] * 0.5,
-    bx = q[0],
-    by = q[1],
-    bz = q[2],
-    bw = q[3];
-  out[0] = bx;
-  out[1] = by;
-  out[2] = bz;
-  out[3] = bw;
-  out[4] = ax * bw + ay * bz - az * by;
-  out[5] = ay * bw + az * bx - ax * bz;
-  out[6] = az * bw + ax * by - ay * bx;
-  out[7] = -ax * bx - ay * by - az * bz;
-  return out;
-}
-
-/**
- * Creates a dual quat from a translation
- *
- * @param {quat2} dual quaternion receiving operation result
- * @param {vec3} t translation vector
- * @returns {quat2} dual quaternion receiving operation result
- * @function
- */
-function fromTranslation(out, t) {
-  out[0] = 0;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  out[4] = t[0] * 0.5;
-  out[5] = t[1] * 0.5;
-  out[6] = t[2] * 0.5;
-  out[7] = 0;
-  return out;
-}
-
-/**
- * Creates a dual quat from a quaternion
- *
- * @param {quat2} dual quaternion receiving operation result
- * @param {quat} q the quaternion
- * @returns {quat2} dual quaternion receiving operation result
- * @function
- */
-function fromRotation(out, q) {
-  out[0] = q[0];
-  out[1] = q[1];
-  out[2] = q[2];
-  out[3] = q[3];
-  out[4] = 0;
-  out[5] = 0;
-  out[6] = 0;
-  out[7] = 0;
-  return out;
-}
-
-/**
- * Creates a new dual quat from a matrix (4x4)
- *
- * @param {quat2} out the dual quaternion
- * @param {mat4} a the matrix
- * @returns {quat2} dual quat receiving operation result
- * @function
- */
-function fromMat4(out, a) {
-  //TODO Optimize this
-  let outer = _quat_js__WEBPACK_IMPORTED_MODULE_1__["create"]();
-  _mat4_js__WEBPACK_IMPORTED_MODULE_2__["getRotation"](outer, a);
-  let t = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](3);
-  _mat4_js__WEBPACK_IMPORTED_MODULE_2__["getTranslation"](t, a);
-  fromRotationTranslation(out, outer, t);
-  return out;
-}
-
-/**
- * Copy the values from one dual quat to another
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the source dual quaternion
- * @returns {quat2} out
- * @function
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  out[4] = a[4];
-  out[5] = a[5];
-  out[6] = a[6];
-  out[7] = a[7];
-  return out;
-}
-
-/**
- * Set a dual quat to the identity dual quaternion
- *
- * @param {quat2} out the receiving quaternion
- * @returns {quat2} out
- */
-function identity(out) {
-  out[0] = 0;
-  out[1] = 0;
-  out[2] = 0;
-  out[3] = 1;
-  out[4] = 0;
-  out[5] = 0;
-  out[6] = 0;
-  out[7] = 0;
-  return out;
-}
-
-/**
- * Set the components of a dual quat to the given values
- *
- * @param {quat2} out the receiving quaternion
- * @param {Number} x1 X component
- * @param {Number} y1 Y component
- * @param {Number} z1 Z component
- * @param {Number} w1 W component
- * @param {Number} x2 X component
- * @param {Number} y2 Y component
- * @param {Number} z2 Z component
- * @param {Number} w2 W component
- * @returns {quat2} out
- * @function
- */
-function set(out, x1, y1, z1, w1, x2, y2, z2, w2) {
-  out[0] = x1;
-  out[1] = y1;
-  out[2] = z1;
-  out[3] = w1;
-
-  out[4] = x2;
-  out[5] = y2;
-  out[6] = z2;
-  out[7] = w2;
-  return out;
-}
-
-/**
- * Gets the real part of a dual quat
- * @param  {quat} out real part
- * @param  {quat2} a Dual Quaternion
- * @return {quat} real part
- */
-const getReal = _quat_js__WEBPACK_IMPORTED_MODULE_1__["copy"];
-
-/**
- * Gets the dual part of a dual quat
- * @param  {quat} out dual part
- * @param  {quat2} a Dual Quaternion
- * @return {quat} dual part
- */
-function getDual(out, a) {
-  out[0] = a[4];
-  out[1] = a[5];
-  out[2] = a[6];
-  out[3] = a[7];
-  return out;
-}
-
-/**
- * Set the real component of a dual quat to the given quaternion
- *
- * @param {quat2} out the receiving quaternion
- * @param {quat} q a quaternion representing the real part
- * @returns {quat2} out
- * @function
- */
-const setReal = _quat_js__WEBPACK_IMPORTED_MODULE_1__["copy"];
-
-/**
- * Set the dual component of a dual quat to the given quaternion
- *
- * @param {quat2} out the receiving quaternion
- * @param {quat} q a quaternion representing the dual part
- * @returns {quat2} out
- * @function
- */
-function setDual(out, q) {
-  out[4] = q[0];
-  out[5] = q[1];
-  out[6] = q[2];
-  out[7] = q[3];
-  return out;
-}
-
-/**
- * Gets the translation of a normalized dual quat
- * @param  {vec3} out translation
- * @param  {quat2} a Dual Quaternion to be decomposed
- * @return {vec3} translation
- */
-function getTranslation(out, a) {
-  let ax = a[4],
-    ay = a[5],
-    az = a[6],
-    aw = a[7],
-    bx = -a[0],
-    by = -a[1],
-    bz = -a[2],
-    bw = a[3];
-  out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
-  out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
-  out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
-  return out;
-}
-
-/**
- * Translates a dual quat by the given vector
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to translate
- * @param {vec3} v vector to translate by
- * @returns {quat2} out
- */
-function translate(out, a, v) {
-  let ax1 = a[0],
-    ay1 = a[1],
-    az1 = a[2],
-    aw1 = a[3],
-    bx1 = v[0] * 0.5,
-    by1 = v[1] * 0.5,
-    bz1 = v[2] * 0.5,
-    ax2 = a[4],
-    ay2 = a[5],
-    az2 = a[6],
-    aw2 = a[7];
-  out[0] = ax1;
-  out[1] = ay1;
-  out[2] = az1;
-  out[3] = aw1;
-  out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2;
-  out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2;
-  out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2;
-  out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2;
-  return out;
-}
-
-/**
- * Rotates a dual quat around the X axis
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to rotate
- * @param {number} rad how far should the rotation be
- * @returns {quat2} out
- */
-function rotateX(out, a, rad) {
-  let bx = -a[0],
-    by = -a[1],
-    bz = -a[2],
-    bw = a[3],
-    ax = a[4],
-    ay = a[5],
-    az = a[6],
-    aw = a[7],
-    ax1 = ax * bw + aw * bx + ay * bz - az * by,
-    ay1 = ay * bw + aw * by + az * bx - ax * bz,
-    az1 = az * bw + aw * bz + ax * by - ay * bx,
-    aw1 = aw * bw - ax * bx - ay * by - az * bz;
-  _quat_js__WEBPACK_IMPORTED_MODULE_1__["rotateX"](out, a, rad);
-  bx = out[0];
-  by = out[1];
-  bz = out[2];
-  bw = out[3];
-  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
-  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
-  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
-  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
-  return out;
-}
-
-/**
- * Rotates a dual quat around the Y axis
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to rotate
- * @param {number} rad how far should the rotation be
- * @returns {quat2} out
- */
-function rotateY(out, a, rad) {
-  let bx = -a[0],
-    by = -a[1],
-    bz = -a[2],
-    bw = a[3],
-    ax = a[4],
-    ay = a[5],
-    az = a[6],
-    aw = a[7],
-    ax1 = ax * bw + aw * bx + ay * bz - az * by,
-    ay1 = ay * bw + aw * by + az * bx - ax * bz,
-    az1 = az * bw + aw * bz + ax * by - ay * bx,
-    aw1 = aw * bw - ax * bx - ay * by - az * bz;
-  _quat_js__WEBPACK_IMPORTED_MODULE_1__["rotateY"](out, a, rad);
-  bx = out[0];
-  by = out[1];
-  bz = out[2];
-  bw = out[3];
-  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
-  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
-  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
-  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
-  return out;
-}
-
-/**
- * Rotates a dual quat around the Z axis
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to rotate
- * @param {number} rad how far should the rotation be
- * @returns {quat2} out
- */
-function rotateZ(out, a, rad) {
-  let bx = -a[0],
-    by = -a[1],
-    bz = -a[2],
-    bw = a[3],
-    ax = a[4],
-    ay = a[5],
-    az = a[6],
-    aw = a[7],
-    ax1 = ax * bw + aw * bx + ay * bz - az * by,
-    ay1 = ay * bw + aw * by + az * bx - ax * bz,
-    az1 = az * bw + aw * bz + ax * by - ay * bx,
-    aw1 = aw * bw - ax * bx - ay * by - az * bz;
-  _quat_js__WEBPACK_IMPORTED_MODULE_1__["rotateZ"](out, a, rad);
-  bx = out[0];
-  by = out[1];
-  bz = out[2];
-  bw = out[3];
-  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
-  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
-  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
-  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
-  return out;
-}
-
-/**
- * Rotates a dual quat by a given quaternion (a * q)
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to rotate
- * @param {quat} q quaternion to rotate by
- * @returns {quat2} out
- */
-function rotateByQuatAppend(out, a, q) {
-  let qx = q[0],
-    qy = q[1],
-    qz = q[2],
-    qw = q[3],
-    ax = a[0],
-    ay = a[1],
-    az = a[2],
-    aw = a[3];
-
-  out[0] = ax * qw + aw * qx + ay * qz - az * qy;
-  out[1] = ay * qw + aw * qy + az * qx - ax * qz;
-  out[2] = az * qw + aw * qz + ax * qy - ay * qx;
-  out[3] = aw * qw - ax * qx - ay * qy - az * qz;
-  ax = a[4];
-  ay = a[5];
-  az = a[6];
-  aw = a[7];
-  out[4] = ax * qw + aw * qx + ay * qz - az * qy;
-  out[5] = ay * qw + aw * qy + az * qx - ax * qz;
-  out[6] = az * qw + aw * qz + ax * qy - ay * qx;
-  out[7] = aw * qw - ax * qx - ay * qy - az * qz;
-  return out;
-}
-
-/**
- * Rotates a dual quat by a given quaternion (q * a)
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat} q quaternion to rotate by
- * @param {quat2} a the dual quaternion to rotate
- * @returns {quat2} out
- */
-function rotateByQuatPrepend(out, q, a) {
-  let qx = q[0],
-    qy = q[1],
-    qz = q[2],
-    qw = q[3],
-    bx = a[0],
-    by = a[1],
-    bz = a[2],
-    bw = a[3];
-
-  out[0] = qx * bw + qw * bx + qy * bz - qz * by;
-  out[1] = qy * bw + qw * by + qz * bx - qx * bz;
-  out[2] = qz * bw + qw * bz + qx * by - qy * bx;
-  out[3] = qw * bw - qx * bx - qy * by - qz * bz;
-  bx = a[4];
-  by = a[5];
-  bz = a[6];
-  bw = a[7];
-  out[4] = qx * bw + qw * bx + qy * bz - qz * by;
-  out[5] = qy * bw + qw * by + qz * bx - qx * bz;
-  out[6] = qz * bw + qw * bz + qx * by - qy * bx;
-  out[7] = qw * bw - qx * bx - qy * by - qz * bz;
-  return out;
-}
-
-/**
- * Rotates a dual quat around a given axis. Does the normalisation automatically
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the dual quaternion to rotate
- * @param {vec3} axis the axis to rotate around
- * @param {Number} rad how far the rotation should be
- * @returns {quat2} out
- */
-function rotateAroundAxis(out, a, axis, rad) {
-  //Special case for rad = 0
-  if (Math.abs(rad) < _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]) {
-    return copy(out, a);
-  }
-  let axisLength = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
-
-  rad = rad * 0.5;
-  let s = Math.sin(rad);
-  let bx = s * axis[0] / axisLength;
-  let by = s * axis[1] / axisLength;
-  let bz = s * axis[2] / axisLength;
-  let bw = Math.cos(rad);
-
-  let ax1 = a[0],
-    ay1 = a[1],
-    az1 = a[2],
-    aw1 = a[3];
-  out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
-  out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
-  out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
-  out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
-
-  let ax = a[4],
-    ay = a[5],
-    az = a[6],
-    aw = a[7];
-  out[4] = ax * bw + aw * bx + ay * bz - az * by;
-  out[5] = ay * bw + aw * by + az * bx - ax * bz;
-  out[6] = az * bw + aw * bz + ax * by - ay * bx;
-  out[7] = aw * bw - ax * bx - ay * by - az * bz;
-
-  return out;
-}
-
-/**
- * Adds two dual quat's
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the first operand
- * @param {quat2} b the second operand
- * @returns {quat2} out
- * @function
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  out[4] = a[4] + b[4];
-  out[5] = a[5] + b[5];
-  out[6] = a[6] + b[6];
-  out[7] = a[7] + b[7];
-  return out;
-}
-
-/**
- * Multiplies two dual quat's
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a the first operand
- * @param {quat2} b the second operand
- * @returns {quat2} out
- */
-function multiply(out, a, b) {
-  let ax0 = a[0],
-    ay0 = a[1],
-    az0 = a[2],
-    aw0 = a[3],
-    bx1 = b[4],
-    by1 = b[5],
-    bz1 = b[6],
-    bw1 = b[7],
-    ax1 = a[4],
-    ay1 = a[5],
-    az1 = a[6],
-    aw1 = a[7],
-    bx0 = b[0],
-    by0 = b[1],
-    bz0 = b[2],
-    bw0 = b[3];
-  out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0;
-  out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0;
-  out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0;
-  out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0;
-  out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0;
-  out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0;
-  out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0;
-  out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0;
-  return out;
-}
-
-/**
- * Alias for {@link quat2.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Scales a dual quat by a scalar number
- *
- * @param {quat2} out the receiving dual quat
- * @param {quat2} a the dual quat to scale
- * @param {Number} b amount to scale the dual quat by
- * @returns {quat2} out
- * @function
- */
-function scale(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  out[4] = a[4] * b;
-  out[5] = a[5] * b;
-  out[6] = a[6] * b;
-  out[7] = a[7] * b;
-  return out;
-}
-
-/**
- * Calculates the dot product of two dual quat's (The dot product of the real parts)
- *
- * @param {quat2} a the first operand
- * @param {quat2} b the second operand
- * @returns {Number} dot product of a and b
- * @function
- */
-const dot = _quat_js__WEBPACK_IMPORTED_MODULE_1__["dot"];
-
-/**
- * Performs a linear interpolation between two dual quats's
- * NOTE: The resulting dual quaternions won't always be normalized (The error is most noticeable when t = 0.5)
- *
- * @param {quat2} out the receiving dual quat
- * @param {quat2} a the first operand
- * @param {quat2} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {quat2} out
- */
-function lerp(out, a, b, t) {
-  let mt = 1 - t;
-  if (dot(a, b) < 0) t = -t;
-
-  out[0] = a[0] * mt + b[0] * t;
-  out[1] = a[1] * mt + b[1] * t;
-  out[2] = a[2] * mt + b[2] * t;
-  out[3] = a[3] * mt + b[3] * t;
-  out[4] = a[4] * mt + b[4] * t;
-  out[5] = a[5] * mt + b[5] * t;
-  out[6] = a[6] * mt + b[6] * t;
-  out[7] = a[7] * mt + b[7] * t;
-
-  return out;
-}
-
-/**
- * Calculates the inverse of a dual quat. If they are normalized, conjugate is cheaper
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a dual quat to calculate inverse of
- * @returns {quat2} out
- */
-function invert(out, a) {
-  let sqlen = squaredLength(a);
-  out[0] = -a[0] / sqlen;
-  out[1] = -a[1] / sqlen;
-  out[2] = -a[2] / sqlen;
-  out[3] = a[3] / sqlen;
-  out[4] = -a[4] / sqlen;
-  out[5] = -a[5] / sqlen;
-  out[6] = -a[6] / sqlen;
-  out[7] = a[7] / sqlen;
-  return out;
-}
-
-/**
- * Calculates the conjugate of a dual quat
- * If the dual quaternion is normalized, this function is faster than quat2.inverse and produces the same result.
- *
- * @param {quat2} out the receiving quaternion
- * @param {quat2} a quat to calculate conjugate of
- * @returns {quat2} out
- */
-function conjugate(out, a) {
-  out[0] = -a[0];
-  out[1] = -a[1];
-  out[2] = -a[2];
-  out[3] = a[3];
-  out[4] = -a[4];
-  out[5] = -a[5];
-  out[6] = -a[6];
-  out[7] = a[7];
-  return out;
-}
-
-/**
- * Calculates the length of a dual quat
- *
- * @param {quat2} a dual quat to calculate length of
- * @returns {Number} length of a
- * @function
- */
-const length = _quat_js__WEBPACK_IMPORTED_MODULE_1__["length"];
-
-/**
- * Alias for {@link quat2.length}
- * @function
- */
-const len = length;
-
-/**
- * Calculates the squared length of a dual quat
- *
- * @param {quat2} a dual quat to calculate squared length of
- * @returns {Number} squared length of a
- * @function
- */
-const squaredLength = _quat_js__WEBPACK_IMPORTED_MODULE_1__["squaredLength"];
-
-/**
- * Alias for {@link quat2.squaredLength}
- * @function
- */
-const sqrLen = squaredLength;
-
-/**
- * Normalize a dual quat
- *
- * @param {quat2} out the receiving dual quaternion
- * @param {quat2} a dual quaternion to normalize
- * @returns {quat2} out
- * @function
- */
-function normalize(out, a) {
-  let magnitude = squaredLength(a);
-  if (magnitude > 0) {
-    magnitude = Math.sqrt(magnitude);
-
-    let a0 = a[0] / magnitude;
-    let a1 = a[1] / magnitude;
-    let a2 = a[2] / magnitude;
-    let a3 = a[3] / magnitude;
-
-    let b0 = a[4];
-    let b1 = a[5];
-    let b2 = a[6];
-    let b3 = a[7];
-
-    let a_dot_b = (a0 * b0) + (a1 * b1) + (a2 * b2) + (a3 * b3);
-
-    out[0] = a0;
-    out[1] = a1;
-    out[2] = a2;
-    out[3] = a3;
-
-    out[4] = (b0 - (a0 * a_dot_b)) / magnitude;
-    out[5] = (b1 - (a1 * a_dot_b)) / magnitude;
-    out[6] = (b2 - (a2 * a_dot_b)) / magnitude;
-    out[7] = (b3 - (a3 * a_dot_b)) / magnitude;
-  }
-  return out;
-}
-
-/**
- * Returns a string representation of a dual quatenion
- *
- * @param {quat2} a dual quaternion to represent as a string
- * @returns {String} string representation of the dual quat
- */
-function str(a) {
-  return 'quat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
-    a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ')';
-}
-
-/**
- * Returns whether or not the dual quaternions have exactly the same elements in the same position (when compared with ===)
- *
- * @param {quat2} a the first dual quaternion.
- * @param {quat2} b the second dual quaternion.
- * @returns {Boolean} true if the dual quaternions are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&
-    a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7];
-}
-
-/**
- * Returns whether or not the dual quaternions have approximately the same elements in the same position.
- *
- * @param {quat2} a the first dual quat.
- * @param {quat2} b the second dual quat.
- * @returns {Boolean} true if the dual quats are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0],
-    a1 = a[1],
-    a2 = a[2],
-    a3 = a[3],
-    a4 = a[4],
-    a5 = a[5],
-    a6 = a[6],
-    a7 = a[7];
-  let b0 = b[0],
-    b1 = b[1],
-    b2 = b[2],
-    b3 = b[3],
-    b4 = b[4],
-    b5 = b[5],
-    b6 = b[6],
-    b7 = b[7];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-    Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-    Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-    Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
-    Math.abs(a4 - b4) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
-    Math.abs(a5 - b5) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
-    Math.abs(a6 - b6) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
-    Math.abs(a7 - b7) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"] * Math.max(1.0, Math.abs(a7), Math.abs(b7)));
-}
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/vec2.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/vec2.js ***!
-  \******************************************************/
-/*! exports provided: create, clone, fromValues, copy, set, add, subtract, multiply, divide, ceil, floor, min, max, round, scale, scaleAndAdd, distance, squaredDistance, length, squaredLength, negate, inverse, normalize, dot, cross, lerp, random, transformMat2, transformMat2d, transformMat3, transformMat4, rotate, angle, str, exactEquals, equals, len, sub, mul, div, dist, sqrDist, sqrLen, forEach */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "divide", function() { return divide; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ceil", function() { return ceil; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "floor", function() { return floor; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round", function() { return round; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scaleAndAdd", function() { return scaleAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distance", function() { return distance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredDistance", function() { return squaredDistance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "length", function() { return length; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredLength", function() { return squaredLength; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "negate", function() { return negate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "inverse", function() { return inverse; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalize", function() { return normalize; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dot", function() { return dot; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "cross", function() { return cross; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "random", function() { return random; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat2", function() { return transformMat2; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat2d", function() { return transformMat2d; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat3", function() { return transformMat3; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat4", function() { return transformMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "angle", function() { return angle; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "len", function() { return len; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "div", function() { return div; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dist", function() { return dist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrDist", function() { return sqrDist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrLen", function() { return sqrLen; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "forEach", function() { return forEach; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 2 Dimensional Vector
- * @module vec2
- */
-
-/**
- * Creates a new, empty vec2
- *
- * @returns {vec2} a new 2D vector
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](2);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[0] = 0;
-    out[1] = 0;
-  }
-  return out;
-}
-
-/**
- * Creates a new vec2 initialized with values from an existing vector
- *
- * @param {vec2} a vector to clone
- * @returns {vec2} a new 2D vector
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](2);
-  out[0] = a[0];
-  out[1] = a[1];
-  return out;
-}
-
-/**
- * Creates a new vec2 initialized with the given values
- *
- * @param {Number} x X component
- * @param {Number} y Y component
- * @returns {vec2} a new 2D vector
- */
-function fromValues(x, y) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](2);
-  out[0] = x;
-  out[1] = y;
-  return out;
-}
-
-/**
- * Copy the values from one vec2 to another
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the source vector
- * @returns {vec2} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  return out;
-}
-
-/**
- * Set the components of a vec2 to the given values
- *
- * @param {vec2} out the receiving vector
- * @param {Number} x X component
- * @param {Number} y Y component
- * @returns {vec2} out
- */
-function set(out, x, y) {
-  out[0] = x;
-  out[1] = y;
-  return out;
-}
-
-/**
- * Adds two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  return out;
-}
-
-/**
- * Subtracts vector b from vector a
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  return out;
-}
-
-/**
- * Multiplies two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function multiply(out, a, b) {
-  out[0] = a[0] * b[0];
-  out[1] = a[1] * b[1];
-  return out;
-}
-
-/**
- * Divides two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function divide(out, a, b) {
-  out[0] = a[0] / b[0];
-  out[1] = a[1] / b[1];
-  return out;
-}
-
-/**
- * Math.ceil the components of a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to ceil
- * @returns {vec2} out
- */
-function ceil(out, a) {
-  out[0] = Math.ceil(a[0]);
-  out[1] = Math.ceil(a[1]);
-  return out;
-}
-
-/**
- * Math.floor the components of a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to floor
- * @returns {vec2} out
- */
-function floor(out, a) {
-  out[0] = Math.floor(a[0]);
-  out[1] = Math.floor(a[1]);
-  return out;
-}
-
-/**
- * Returns the minimum of two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function min(out, a, b) {
-  out[0] = Math.min(a[0], b[0]);
-  out[1] = Math.min(a[1], b[1]);
-  return out;
-}
-
-/**
- * Returns the maximum of two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec2} out
- */
-function max(out, a, b) {
-  out[0] = Math.max(a[0], b[0]);
-  out[1] = Math.max(a[1], b[1]);
-  return out;
-}
-
-/**
- * Math.round the components of a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to round
- * @returns {vec2} out
- */
-function round (out, a) {
-  out[0] = Math.round(a[0]);
-  out[1] = Math.round(a[1]);
-  return out;
-}
-
-/**
- * Scales a vec2 by a scalar number
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the vector to scale
- * @param {Number} b amount to scale the vector by
- * @returns {vec2} out
- */
-function scale(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  return out;
-}
-
-/**
- * Adds two vec2's after scaling the second operand by a scalar value
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @param {Number} scale the amount to scale b by before adding
- * @returns {vec2} out
- */
-function scaleAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  return out;
-}
-
-/**
- * Calculates the euclidian distance between two vec2's
- *
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {Number} distance between a and b
- */
-function distance(a, b) {
-  var x = b[0] - a[0],
-    y = b[1] - a[1];
-  return Math.sqrt(x*x + y*y);
-}
-
-/**
- * Calculates the squared euclidian distance between two vec2's
- *
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {Number} squared distance between a and b
- */
-function squaredDistance(a, b) {
-  var x = b[0] - a[0],
-    y = b[1] - a[1];
-  return x*x + y*y;
-}
-
-/**
- * Calculates the length of a vec2
- *
- * @param {vec2} a vector to calculate length of
- * @returns {Number} length of a
- */
-function length(a) {
-  var x = a[0],
-    y = a[1];
-  return Math.sqrt(x*x + y*y);
-}
-
-/**
- * Calculates the squared length of a vec2
- *
- * @param {vec2} a vector to calculate squared length of
- * @returns {Number} squared length of a
- */
-function squaredLength (a) {
-  var x = a[0],
-    y = a[1];
-  return x*x + y*y;
-}
-
-/**
- * Negates the components of a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to negate
- * @returns {vec2} out
- */
-function negate(out, a) {
-  out[0] = -a[0];
-  out[1] = -a[1];
-  return out;
-}
-
-/**
- * Returns the inverse of the components of a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to invert
- * @returns {vec2} out
- */
-function inverse(out, a) {
-  out[0] = 1.0 / a[0];
-  out[1] = 1.0 / a[1];
-  return out;
-}
-
-/**
- * Normalize a vec2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a vector to normalize
- * @returns {vec2} out
- */
-function normalize(out, a) {
-  var x = a[0],
-    y = a[1];
-  var len = x*x + y*y;
-  if (len > 0) {
-    //TODO: evaluate use of glm_invsqrt here?
-    len = 1 / Math.sqrt(len);
-    out[0] = a[0] * len;
-    out[1] = a[1] * len;
-  }
-  return out;
-}
-
-/**
- * Calculates the dot product of two vec2's
- *
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {Number} dot product of a and b
- */
-function dot(a, b) {
-  return a[0] * b[0] + a[1] * b[1];
-}
-
-/**
- * Computes the cross product of two vec2's
- * Note that the cross product must by definition produce a 3D vector
- *
- * @param {vec3} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @returns {vec3} out
- */
-function cross(out, a, b) {
-  var z = a[0] * b[1] - a[1] * b[0];
-  out[0] = out[1] = 0;
-  out[2] = z;
-  return out;
-}
-
-/**
- * Performs a linear interpolation between two vec2's
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the first operand
- * @param {vec2} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {vec2} out
- */
-function lerp(out, a, b, t) {
-  var ax = a[0],
-    ay = a[1];
-  out[0] = ax + t * (b[0] - ax);
-  out[1] = ay + t * (b[1] - ay);
-  return out;
-}
-
-/**
- * Generates a random vector with the given scale
- *
- * @param {vec2} out the receiving vector
- * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
- * @returns {vec2} out
- */
-function random(out, scale) {
-  scale = scale || 1.0;
-  var r = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2.0 * Math.PI;
-  out[0] = Math.cos(r) * scale;
-  out[1] = Math.sin(r) * scale;
-  return out;
-}
-
-/**
- * Transforms the vec2 with a mat2
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the vector to transform
- * @param {mat2} m matrix to transform with
- * @returns {vec2} out
- */
-function transformMat2(out, a, m) {
-  var x = a[0],
-    y = a[1];
-  out[0] = m[0] * x + m[2] * y;
-  out[1] = m[1] * x + m[3] * y;
-  return out;
-}
-
-/**
- * Transforms the vec2 with a mat2d
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the vector to transform
- * @param {mat2d} m matrix to transform with
- * @returns {vec2} out
- */
-function transformMat2d(out, a, m) {
-  var x = a[0],
-    y = a[1];
-  out[0] = m[0] * x + m[2] * y + m[4];
-  out[1] = m[1] * x + m[3] * y + m[5];
-  return out;
-}
-
-/**
- * Transforms the vec2 with a mat3
- * 3rd vector component is implicitly '1'
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the vector to transform
- * @param {mat3} m matrix to transform with
- * @returns {vec2} out
- */
-function transformMat3(out, a, m) {
-  var x = a[0],
-    y = a[1];
-  out[0] = m[0] * x + m[3] * y + m[6];
-  out[1] = m[1] * x + m[4] * y + m[7];
-  return out;
-}
-
-/**
- * Transforms the vec2 with a mat4
- * 3rd vector component is implicitly '0'
- * 4th vector component is implicitly '1'
- *
- * @param {vec2} out the receiving vector
- * @param {vec2} a the vector to transform
- * @param {mat4} m matrix to transform with
- * @returns {vec2} out
- */
-function transformMat4(out, a, m) {
-  let x = a[0];
-  let y = a[1];
-  out[0] = m[0] * x + m[4] * y + m[12];
-  out[1] = m[1] * x + m[5] * y + m[13];
-  return out;
-}
-
-/**
- * Rotate a 2D vector
- * @param {vec2} out The receiving vec2
- * @param {vec2} a The vec2 point to rotate
- * @param {vec2} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @returns {vec2} out
- */
-function rotate(out, a, b, c) {
-  //Translate point to the origin
-  let p0 = a[0] - b[0],
-  p1 = a[1] - b[1],
-  sinC = Math.sin(c),
-  cosC = Math.cos(c);
-  
-  //perform rotation and translate to correct position
-  out[0] = p0*cosC - p1*sinC + b[0];
-  out[1] = p0*sinC + p1*cosC + b[1];
-
-  return out;
-}
-
-/**
- * Get the angle between two 2D vectors
- * @param {vec2} a The first operand
- * @param {vec2} b The second operand
- * @returns {Number} The angle in radians
- */
-function angle(a, b) {
-  let x1 = a[0],
-    y1 = a[1],
-    x2 = b[0],
-    y2 = b[1];
-  
-  let len1 = x1*x1 + y1*y1;
-  if (len1 > 0) {
-    //TODO: evaluate use of glm_invsqrt here?
-    len1 = 1 / Math.sqrt(len1);
-  }
-  
-  let len2 = x2*x2 + y2*y2;
-  if (len2 > 0) {
-    //TODO: evaluate use of glm_invsqrt here?
-    len2 = 1 / Math.sqrt(len2);
-  }
-  
-  let cosine = (x1 * x2 + y1 * y2) * len1 * len2;
-  
-  
-  if(cosine > 1.0) {
-    return 0;
-  }
-  else if(cosine < -1.0) {
-    return Math.PI;
-  } else {
-    return Math.acos(cosine);
-  }
-}
-
-/**
- * Returns a string representation of a vector
- *
- * @param {vec2} a vector to represent as a string
- * @returns {String} string representation of the vector
- */
-function str(a) {
-  return 'vec2(' + a[0] + ', ' + a[1] + ')';
-}
-
-/**
- * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===)
- *
- * @param {vec2} a The first vector.
- * @param {vec2} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1];
-}
-
-/**
- * Returns whether or not the vectors have approximately the same elements in the same position.
- *
- * @param {vec2} a The first vector.
- * @param {vec2} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1];
-  let b0 = b[0], b1 = b[1];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)));
-}
-
-/**
- * Alias for {@link vec2.length}
- * @function
- */
-const len = length;
-
-/**
- * Alias for {@link vec2.subtract}
- * @function
- */
-const sub = subtract;
-
-/**
- * Alias for {@link vec2.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link vec2.divide}
- * @function
- */
-const div = divide;
-
-/**
- * Alias for {@link vec2.distance}
- * @function
- */
-const dist = distance;
-
-/**
- * Alias for {@link vec2.squaredDistance}
- * @function
- */
-const sqrDist = squaredDistance;
-
-/**
- * Alias for {@link vec2.squaredLength}
- * @function
- */
-const sqrLen = squaredLength;
-
-/**
- * Perform some operation over an array of vec2s.
- *
- * @param {Array} a the array of vectors to iterate over
- * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
- * @param {Number} offset Number of elements to skip at the beginning of the array
- * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
- * @param {Function} fn Function to call for each vector in the array
- * @param {Object} [arg] additional argument to pass to fn
- * @returns {Array} a
- * @function
- */
-const forEach = (function() {
-  let vec = create();
-
-  return function(a, stride, offset, count, fn, arg) {
-    let i, l;
-    if(!stride) {
-      stride = 2;
-    }
-
-    if(!offset) {
-      offset = 0;
-    }
-
-    if(count) {
-      l = Math.min((count * stride) + offset, a.length);
-    } else {
-      l = a.length;
-    }
-
-    for(i = offset; i < l; i += stride) {
-      vec[0] = a[i]; vec[1] = a[i+1];
-      fn(vec, vec, arg);
-      a[i] = vec[0]; a[i+1] = vec[1];
-    }
-
-    return a;
-  };
-})();
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/vec3.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/vec3.js ***!
-  \******************************************************/
-/*! exports provided: create, clone, length, fromValues, copy, set, add, subtract, multiply, divide, ceil, floor, min, max, round, scale, scaleAndAdd, distance, squaredDistance, squaredLength, negate, inverse, normalize, dot, cross, lerp, hermite, bezier, random, transformMat4, transformMat3, transformQuat, rotateX, rotateY, rotateZ, angle, str, exactEquals, equals, sub, mul, div, dist, sqrDist, len, sqrLen, forEach */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "length", function() { return length; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "divide", function() { return divide; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ceil", function() { return ceil; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "floor", function() { return floor; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round", function() { return round; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scaleAndAdd", function() { return scaleAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distance", function() { return distance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredDistance", function() { return squaredDistance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredLength", function() { return squaredLength; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "negate", function() { return negate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "inverse", function() { return inverse; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalize", function() { return normalize; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dot", function() { return dot; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "cross", function() { return cross; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hermite", function() { return hermite; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bezier", function() { return bezier; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "random", function() { return random; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat4", function() { return transformMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat3", function() { return transformMat3; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformQuat", function() { return transformQuat; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateX", function() { return rotateX; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateY", function() { return rotateY; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rotateZ", function() { return rotateZ; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "angle", function() { return angle; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "div", function() { return div; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dist", function() { return dist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrDist", function() { return sqrDist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "len", function() { return len; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrLen", function() { return sqrLen; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "forEach", function() { return forEach; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 3 Dimensional Vector
- * @module vec3
- */
-
-/**
- * Creates a new, empty vec3
- *
- * @returns {vec3} a new 3D vector
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](3);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[0] = 0;
-    out[1] = 0;
-    out[2] = 0;
-  }
-  return out;
-}
-
-/**
- * Creates a new vec3 initialized with values from an existing vector
- *
- * @param {vec3} a vector to clone
- * @returns {vec3} a new 3D vector
- */
-function clone(a) {
-  var out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](3);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  return out;
-}
-
-/**
- * Calculates the length of a vec3
- *
- * @param {vec3} a vector to calculate length of
- * @returns {Number} length of a
- */
-function length(a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  return Math.sqrt(x*x + y*y + z*z);
-}
-
-/**
- * Creates a new vec3 initialized with the given values
- *
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @returns {vec3} a new 3D vector
- */
-function fromValues(x, y, z) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](3);
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-  return out;
-}
-
-/**
- * Copy the values from one vec3 to another
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the source vector
- * @returns {vec3} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  return out;
-}
-
-/**
- * Set the components of a vec3 to the given values
- *
- * @param {vec3} out the receiving vector
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @returns {vec3} out
- */
-function set(out, x, y, z) {
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-  return out;
-}
-
-/**
- * Adds two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  return out;
-}
-
-/**
- * Subtracts vector b from vector a
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  return out;
-}
-
-/**
- * Multiplies two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function multiply(out, a, b) {
-  out[0] = a[0] * b[0];
-  out[1] = a[1] * b[1];
-  out[2] = a[2] * b[2];
-  return out;
-}
-
-/**
- * Divides two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function divide(out, a, b) {
-  out[0] = a[0] / b[0];
-  out[1] = a[1] / b[1];
-  out[2] = a[2] / b[2];
-  return out;
-}
-
-/**
- * Math.ceil the components of a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to ceil
- * @returns {vec3} out
- */
-function ceil(out, a) {
-  out[0] = Math.ceil(a[0]);
-  out[1] = Math.ceil(a[1]);
-  out[2] = Math.ceil(a[2]);
-  return out;
-}
-
-/**
- * Math.floor the components of a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to floor
- * @returns {vec3} out
- */
-function floor(out, a) {
-  out[0] = Math.floor(a[0]);
-  out[1] = Math.floor(a[1]);
-  out[2] = Math.floor(a[2]);
-  return out;
-}
-
-/**
- * Returns the minimum of two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function min(out, a, b) {
-  out[0] = Math.min(a[0], b[0]);
-  out[1] = Math.min(a[1], b[1]);
-  out[2] = Math.min(a[2], b[2]);
-  return out;
-}
-
-/**
- * Returns the maximum of two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function max(out, a, b) {
-  out[0] = Math.max(a[0], b[0]);
-  out[1] = Math.max(a[1], b[1]);
-  out[2] = Math.max(a[2], b[2]);
-  return out;
-}
-
-/**
- * Math.round the components of a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to round
- * @returns {vec3} out
- */
-function round(out, a) {
-  out[0] = Math.round(a[0]);
-  out[1] = Math.round(a[1]);
-  out[2] = Math.round(a[2]);
-  return out;
-}
-
-/**
- * Scales a vec3 by a scalar number
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the vector to scale
- * @param {Number} b amount to scale the vector by
- * @returns {vec3} out
- */
-function scale(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  return out;
-}
-
-/**
- * Adds two vec3's after scaling the second operand by a scalar value
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @param {Number} scale the amount to scale b by before adding
- * @returns {vec3} out
- */
-function scaleAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  return out;
-}
-
-/**
- * Calculates the euclidian distance between two vec3's
- *
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {Number} distance between a and b
- */
-function distance(a, b) {
-  let x = b[0] - a[0];
-  let y = b[1] - a[1];
-  let z = b[2] - a[2];
-  return Math.sqrt(x*x + y*y + z*z);
-}
-
-/**
- * Calculates the squared euclidian distance between two vec3's
- *
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {Number} squared distance between a and b
- */
-function squaredDistance(a, b) {
-  let x = b[0] - a[0];
-  let y = b[1] - a[1];
-  let z = b[2] - a[2];
-  return x*x + y*y + z*z;
-}
-
-/**
- * Calculates the squared length of a vec3
- *
- * @param {vec3} a vector to calculate squared length of
- * @returns {Number} squared length of a
- */
-function squaredLength(a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  return x*x + y*y + z*z;
-}
-
-/**
- * Negates the components of a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to negate
- * @returns {vec3} out
- */
-function negate(out, a) {
-  out[0] = -a[0];
-  out[1] = -a[1];
-  out[2] = -a[2];
-  return out;
-}
-
-/**
- * Returns the inverse of the components of a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to invert
- * @returns {vec3} out
- */
-function inverse(out, a) {
-  out[0] = 1.0 / a[0];
-  out[1] = 1.0 / a[1];
-  out[2] = 1.0 / a[2];
-  return out;
-}
-
-/**
- * Normalize a vec3
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a vector to normalize
- * @returns {vec3} out
- */
-function normalize(out, a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  let len = x*x + y*y + z*z;
-  if (len > 0) {
-    //TODO: evaluate use of glm_invsqrt here?
-    len = 1 / Math.sqrt(len);
-    out[0] = a[0] * len;
-    out[1] = a[1] * len;
-    out[2] = a[2] * len;
-  }
-  return out;
-}
-
-/**
- * Calculates the dot product of two vec3's
- *
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {Number} dot product of a and b
- */
-function dot(a, b) {
-  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
-}
-
-/**
- * Computes the cross product of two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @returns {vec3} out
- */
-function cross(out, a, b) {
-  let ax = a[0], ay = a[1], az = a[2];
-  let bx = b[0], by = b[1], bz = b[2];
-
-  out[0] = ay * bz - az * by;
-  out[1] = az * bx - ax * bz;
-  out[2] = ax * by - ay * bx;
-  return out;
-}
-
-/**
- * Performs a linear interpolation between two vec3's
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {vec3} out
- */
-function lerp(out, a, b, t) {
-  let ax = a[0];
-  let ay = a[1];
-  let az = a[2];
-  out[0] = ax + t * (b[0] - ax);
-  out[1] = ay + t * (b[1] - ay);
-  out[2] = az + t * (b[2] - az);
-  return out;
-}
-
-/**
- * Performs a hermite interpolation with two control points
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @param {vec3} c the third operand
- * @param {vec3} d the fourth operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {vec3} out
- */
-function hermite(out, a, b, c, d, t) {
-  let factorTimes2 = t * t;
-  let factor1 = factorTimes2 * (2 * t - 3) + 1;
-  let factor2 = factorTimes2 * (t - 2) + t;
-  let factor3 = factorTimes2 * (t - 1);
-  let factor4 = factorTimes2 * (3 - 2 * t);
-
-  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
-  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
-  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
-
-  return out;
-}
-
-/**
- * Performs a bezier interpolation with two control points
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the first operand
- * @param {vec3} b the second operand
- * @param {vec3} c the third operand
- * @param {vec3} d the fourth operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {vec3} out
- */
-function bezier(out, a, b, c, d, t) {
-  let inverseFactor = 1 - t;
-  let inverseFactorTimesTwo = inverseFactor * inverseFactor;
-  let factorTimes2 = t * t;
-  let factor1 = inverseFactorTimesTwo * inverseFactor;
-  let factor2 = 3 * t * inverseFactorTimesTwo;
-  let factor3 = 3 * factorTimes2 * inverseFactor;
-  let factor4 = factorTimes2 * t;
-
-  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
-  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
-  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
-
-  return out;
-}
-
-/**
- * Generates a random vector with the given scale
- *
- * @param {vec3} out the receiving vector
- * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
- * @returns {vec3} out
- */
-function random(out, scale) {
-  scale = scale || 1.0;
-
-  let r = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2.0 * Math.PI;
-  let z = (_common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2.0) - 1.0;
-  let zScale = Math.sqrt(1.0-z*z) * scale;
-
-  out[0] = Math.cos(r) * zScale;
-  out[1] = Math.sin(r) * zScale;
-  out[2] = z * scale;
-  return out;
-}
-
-/**
- * Transforms the vec3 with a mat4.
- * 4th vector component is implicitly '1'
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the vector to transform
- * @param {mat4} m matrix to transform with
- * @returns {vec3} out
- */
-function transformMat4(out, a, m) {
-  let x = a[0], y = a[1], z = a[2];
-  let w = m[3] * x + m[7] * y + m[11] * z + m[15];
-  w = w || 1.0;
-  out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
-  out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
-  out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
-  return out;
-}
-
-/**
- * Transforms the vec3 with a mat3.
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the vector to transform
- * @param {mat3} m the 3x3 matrix to transform with
- * @returns {vec3} out
- */
-function transformMat3(out, a, m) {
-  let x = a[0], y = a[1], z = a[2];
-  out[0] = x * m[0] + y * m[3] + z * m[6];
-  out[1] = x * m[1] + y * m[4] + z * m[7];
-  out[2] = x * m[2] + y * m[5] + z * m[8];
-  return out;
-}
-
-/**
- * Transforms the vec3 with a quat
- * Can also be used for dual quaternions. (Multiply it with the real part)
- *
- * @param {vec3} out the receiving vector
- * @param {vec3} a the vector to transform
- * @param {quat} q quaternion to transform with
- * @returns {vec3} out
- */
-function transformQuat(out, a, q) {
-    // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed
-    let qx = q[0], qy = q[1], qz = q[2], qw = q[3];
-    let x = a[0], y = a[1], z = a[2];
-    // var qvec = [qx, qy, qz];
-    // var uv = vec3.cross([], qvec, a);
-    let uvx = qy * z - qz * y,
-        uvy = qz * x - qx * z,
-        uvz = qx * y - qy * x;
-    // var uuv = vec3.cross([], qvec, uv);
-    let uuvx = qy * uvz - qz * uvy,
-        uuvy = qz * uvx - qx * uvz,
-        uuvz = qx * uvy - qy * uvx;
-    // vec3.scale(uv, uv, 2 * w);
-    let w2 = qw * 2;
-    uvx *= w2;
-    uvy *= w2;
-    uvz *= w2;
-    // vec3.scale(uuv, uuv, 2);
-    uuvx *= 2;
-    uuvy *= 2;
-    uuvz *= 2;
-    // return vec3.add(out, a, vec3.add(out, uv, uuv));
-    out[0] = x + uvx + uuvx;
-    out[1] = y + uvy + uuvy;
-    out[2] = z + uvz + uuvz;
-    return out;
-}
-
-/**
- * Rotate a 3D vector around the x-axis
- * @param {vec3} out The receiving vec3
- * @param {vec3} a The vec3 point to rotate
- * @param {vec3} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @returns {vec3} out
- */
-function rotateX(out, a, b, c){
-  let p = [], r=[];
-  //Translate point to the origin
-  p[0] = a[0] - b[0];
-  p[1] = a[1] - b[1];
-  p[2] = a[2] - b[2];
-
-  //perform rotation
-  r[0] = p[0];
-  r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c);
-  r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c);
-
-  //translate to correct position
-  out[0] = r[0] + b[0];
-  out[1] = r[1] + b[1];
-  out[2] = r[2] + b[2];
-
-  return out;
-}
-
-/**
- * Rotate a 3D vector around the y-axis
- * @param {vec3} out The receiving vec3
- * @param {vec3} a The vec3 point to rotate
- * @param {vec3} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @returns {vec3} out
- */
-function rotateY(out, a, b, c){
-  let p = [], r=[];
-  //Translate point to the origin
-  p[0] = a[0] - b[0];
-  p[1] = a[1] - b[1];
-  p[2] = a[2] - b[2];
-
-  //perform rotation
-  r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c);
-  r[1] = p[1];
-  r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c);
-
-  //translate to correct position
-  out[0] = r[0] + b[0];
-  out[1] = r[1] + b[1];
-  out[2] = r[2] + b[2];
-
-  return out;
-}
-
-/**
- * Rotate a 3D vector around the z-axis
- * @param {vec3} out The receiving vec3
- * @param {vec3} a The vec3 point to rotate
- * @param {vec3} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @returns {vec3} out
- */
-function rotateZ(out, a, b, c){
-  let p = [], r=[];
-  //Translate point to the origin
-  p[0] = a[0] - b[0];
-  p[1] = a[1] - b[1];
-  p[2] = a[2] - b[2];
-
-  //perform rotation
-  r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c);
-  r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c);
-  r[2] = p[2];
-
-  //translate to correct position
-  out[0] = r[0] + b[0];
-  out[1] = r[1] + b[1];
-  out[2] = r[2] + b[2];
-
-  return out;
-}
-
-/**
- * Get the angle between two 3D vectors
- * @param {vec3} a The first operand
- * @param {vec3} b The second operand
- * @returns {Number} The angle in radians
- */
-function angle(a, b) {
-  let tempA = fromValues(a[0], a[1], a[2]);
-  let tempB = fromValues(b[0], b[1], b[2]);
-
-  normalize(tempA, tempA);
-  normalize(tempB, tempB);
-
-  let cosine = dot(tempA, tempB);
-
-  if(cosine > 1.0) {
-    return 0;
-  }
-  else if(cosine < -1.0) {
-    return Math.PI;
-  } else {
-    return Math.acos(cosine);
-  }
-}
-
-/**
- * Returns a string representation of a vector
- *
- * @param {vec3} a vector to represent as a string
- * @returns {String} string representation of the vector
- */
-function str(a) {
-  return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')';
-}
-
-/**
- * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
- *
- * @param {vec3} a The first vector.
- * @param {vec3} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
-}
-
-/**
- * Returns whether or not the vectors have approximately the same elements in the same position.
- *
- * @param {vec3} a The first vector.
- * @param {vec3} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2];
-  let b0 = b[0], b1 = b[1], b2 = b[2];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)));
-}
-
-/**
- * Alias for {@link vec3.subtract}
- * @function
- */
-const sub = subtract;
-
-/**
- * Alias for {@link vec3.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link vec3.divide}
- * @function
- */
-const div = divide;
-
-/**
- * Alias for {@link vec3.distance}
- * @function
- */
-const dist = distance;
-
-/**
- * Alias for {@link vec3.squaredDistance}
- * @function
- */
-const sqrDist = squaredDistance;
-
-/**
- * Alias for {@link vec3.length}
- * @function
- */
-const len = length;
-
-/**
- * Alias for {@link vec3.squaredLength}
- * @function
- */
-const sqrLen = squaredLength;
-
-/**
- * Perform some operation over an array of vec3s.
- *
- * @param {Array} a the array of vectors to iterate over
- * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
- * @param {Number} offset Number of elements to skip at the beginning of the array
- * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
- * @param {Function} fn Function to call for each vector in the array
- * @param {Object} [arg] additional argument to pass to fn
- * @returns {Array} a
- * @function
- */
-const forEach = (function() {
-  let vec = create();
-
-  return function(a, stride, offset, count, fn, arg) {
-    let i, l;
-    if(!stride) {
-      stride = 3;
-    }
-
-    if(!offset) {
-      offset = 0;
-    }
-
-    if(count) {
-      l = Math.min((count * stride) + offset, a.length);
-    } else {
-      l = a.length;
-    }
-
-    for(i = offset; i < l; i += stride) {
-      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2];
-      fn(vec, vec, arg);
-      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2];
-    }
-
-    return a;
-  };
-})();
-
-
-/***/ }),
-
-/***/ "./node_modules/gl-matrix/src/gl-matrix/vec4.js":
-/*!******************************************************!*\
-  !*** ./node_modules/gl-matrix/src/gl-matrix/vec4.js ***!
-  \******************************************************/
-/*! exports provided: create, clone, fromValues, copy, set, add, subtract, multiply, divide, ceil, floor, min, max, round, scale, scaleAndAdd, distance, squaredDistance, length, squaredLength, negate, inverse, normalize, dot, lerp, random, transformMat4, transformQuat, str, exactEquals, equals, sub, mul, div, dist, sqrDist, len, sqrLen, forEach */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "create", function() { return create; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "clone", function() { return clone; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromValues", function() { return fromValues; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copy", function() { return copy; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "set", function() { return set; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subtract", function() { return subtract; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multiply", function() { return multiply; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "divide", function() { return divide; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ceil", function() { return ceil; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "floor", function() { return floor; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "round", function() { return round; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scaleAndAdd", function() { return scaleAndAdd; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distance", function() { return distance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredDistance", function() { return squaredDistance; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "length", function() { return length; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "squaredLength", function() { return squaredLength; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "negate", function() { return negate; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "inverse", function() { return inverse; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "normalize", function() { return normalize; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dot", function() { return dot; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lerp", function() { return lerp; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "random", function() { return random; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformMat4", function() { return transformMat4; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformQuat", function() { return transformQuat; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exactEquals", function() { return exactEquals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "equals", function() { return equals; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sub", function() { return sub; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mul", function() { return mul; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "div", function() { return div; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dist", function() { return dist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrDist", function() { return sqrDist; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "len", function() { return len; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqrLen", function() { return sqrLen; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "forEach", function() { return forEach; });
-/* harmony import */ var _common_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-
-/**
- * 4 Dimensional Vector
- * @module vec4
- */
-
-/**
- * Creates a new, empty vec4
- *
- * @returns {vec4} a new 4D vector
- */
-function create() {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  if(_common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"] != Float32Array) {
-    out[0] = 0;
-    out[1] = 0;
-    out[2] = 0;
-    out[3] = 0;
-  }
-  return out;
-}
-
-/**
- * Creates a new vec4 initialized with values from an existing vector
- *
- * @param {vec4} a vector to clone
- * @returns {vec4} a new 4D vector
- */
-function clone(a) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Creates a new vec4 initialized with the given values
- *
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @param {Number} w W component
- * @returns {vec4} a new 4D vector
- */
-function fromValues(x, y, z, w) {
-  let out = new _common_js__WEBPACK_IMPORTED_MODULE_0__["ARRAY_TYPE"](4);
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-  out[3] = w;
-  return out;
-}
-
-/**
- * Copy the values from one vec4 to another
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the source vector
- * @returns {vec4} out
- */
-function copy(out, a) {
-  out[0] = a[0];
-  out[1] = a[1];
-  out[2] = a[2];
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Set the components of a vec4 to the given values
- *
- * @param {vec4} out the receiving vector
- * @param {Number} x X component
- * @param {Number} y Y component
- * @param {Number} z Z component
- * @param {Number} w W component
- * @returns {vec4} out
- */
-function set(out, x, y, z, w) {
-  out[0] = x;
-  out[1] = y;
-  out[2] = z;
-  out[3] = w;
-  return out;
-}
-
-/**
- * Adds two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function add(out, a, b) {
-  out[0] = a[0] + b[0];
-  out[1] = a[1] + b[1];
-  out[2] = a[2] + b[2];
-  out[3] = a[3] + b[3];
-  return out;
-}
-
-/**
- * Subtracts vector b from vector a
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function subtract(out, a, b) {
-  out[0] = a[0] - b[0];
-  out[1] = a[1] - b[1];
-  out[2] = a[2] - b[2];
-  out[3] = a[3] - b[3];
-  return out;
-}
-
-/**
- * Multiplies two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function multiply(out, a, b) {
-  out[0] = a[0] * b[0];
-  out[1] = a[1] * b[1];
-  out[2] = a[2] * b[2];
-  out[3] = a[3] * b[3];
-  return out;
-}
-
-/**
- * Divides two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function divide(out, a, b) {
-  out[0] = a[0] / b[0];
-  out[1] = a[1] / b[1];
-  out[2] = a[2] / b[2];
-  out[3] = a[3] / b[3];
-  return out;
-}
-
-/**
- * Math.ceil the components of a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to ceil
- * @returns {vec4} out
- */
-function ceil(out, a) {
-  out[0] = Math.ceil(a[0]);
-  out[1] = Math.ceil(a[1]);
-  out[2] = Math.ceil(a[2]);
-  out[3] = Math.ceil(a[3]);
-  return out;
-}
-
-/**
- * Math.floor the components of a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to floor
- * @returns {vec4} out
- */
-function floor(out, a) {
-  out[0] = Math.floor(a[0]);
-  out[1] = Math.floor(a[1]);
-  out[2] = Math.floor(a[2]);
-  out[3] = Math.floor(a[3]);
-  return out;
-}
-
-/**
- * Returns the minimum of two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function min(out, a, b) {
-  out[0] = Math.min(a[0], b[0]);
-  out[1] = Math.min(a[1], b[1]);
-  out[2] = Math.min(a[2], b[2]);
-  out[3] = Math.min(a[3], b[3]);
-  return out;
-}
-
-/**
- * Returns the maximum of two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {vec4} out
- */
-function max(out, a, b) {
-  out[0] = Math.max(a[0], b[0]);
-  out[1] = Math.max(a[1], b[1]);
-  out[2] = Math.max(a[2], b[2]);
-  out[3] = Math.max(a[3], b[3]);
-  return out;
-}
-
-/**
- * Math.round the components of a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to round
- * @returns {vec4} out
- */
-function round(out, a) {
-  out[0] = Math.round(a[0]);
-  out[1] = Math.round(a[1]);
-  out[2] = Math.round(a[2]);
-  out[3] = Math.round(a[3]);
-  return out;
-}
-
-/**
- * Scales a vec4 by a scalar number
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the vector to scale
- * @param {Number} b amount to scale the vector by
- * @returns {vec4} out
- */
-function scale(out, a, b) {
-  out[0] = a[0] * b;
-  out[1] = a[1] * b;
-  out[2] = a[2] * b;
-  out[3] = a[3] * b;
-  return out;
-}
-
-/**
- * Adds two vec4's after scaling the second operand by a scalar value
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @param {Number} scale the amount to scale b by before adding
- * @returns {vec4} out
- */
-function scaleAndAdd(out, a, b, scale) {
-  out[0] = a[0] + (b[0] * scale);
-  out[1] = a[1] + (b[1] * scale);
-  out[2] = a[2] + (b[2] * scale);
-  out[3] = a[3] + (b[3] * scale);
-  return out;
-}
-
-/**
- * Calculates the euclidian distance between two vec4's
- *
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {Number} distance between a and b
- */
-function distance(a, b) {
-  let x = b[0] - a[0];
-  let y = b[1] - a[1];
-  let z = b[2] - a[2];
-  let w = b[3] - a[3];
-  return Math.sqrt(x*x + y*y + z*z + w*w);
-}
-
-/**
- * Calculates the squared euclidian distance between two vec4's
- *
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {Number} squared distance between a and b
- */
-function squaredDistance(a, b) {
-  let x = b[0] - a[0];
-  let y = b[1] - a[1];
-  let z = b[2] - a[2];
-  let w = b[3] - a[3];
-  return x*x + y*y + z*z + w*w;
-}
-
-/**
- * Calculates the length of a vec4
- *
- * @param {vec4} a vector to calculate length of
- * @returns {Number} length of a
- */
-function length(a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  let w = a[3];
-  return Math.sqrt(x*x + y*y + z*z + w*w);
-}
-
-/**
- * Calculates the squared length of a vec4
- *
- * @param {vec4} a vector to calculate squared length of
- * @returns {Number} squared length of a
- */
-function squaredLength(a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  let w = a[3];
-  return x*x + y*y + z*z + w*w;
-}
-
-/**
- * Negates the components of a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to negate
- * @returns {vec4} out
- */
-function negate(out, a) {
-  out[0] = -a[0];
-  out[1] = -a[1];
-  out[2] = -a[2];
-  out[3] = -a[3];
-  return out;
-}
-
-/**
- * Returns the inverse of the components of a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to invert
- * @returns {vec4} out
- */
-function inverse(out, a) {
-  out[0] = 1.0 / a[0];
-  out[1] = 1.0 / a[1];
-  out[2] = 1.0 / a[2];
-  out[3] = 1.0 / a[3];
-  return out;
-}
-
-/**
- * Normalize a vec4
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a vector to normalize
- * @returns {vec4} out
- */
-function normalize(out, a) {
-  let x = a[0];
-  let y = a[1];
-  let z = a[2];
-  let w = a[3];
-  let len = x*x + y*y + z*z + w*w;
-  if (len > 0) {
-    len = 1 / Math.sqrt(len);
-    out[0] = x * len;
-    out[1] = y * len;
-    out[2] = z * len;
-    out[3] = w * len;
-  }
-  return out;
-}
-
-/**
- * Calculates the dot product of two vec4's
- *
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @returns {Number} dot product of a and b
- */
-function dot(a, b) {
-  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
-}
-
-/**
- * Performs a linear interpolation between two vec4's
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the first operand
- * @param {vec4} b the second operand
- * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
- * @returns {vec4} out
- */
-function lerp(out, a, b, t) {
-  let ax = a[0];
-  let ay = a[1];
-  let az = a[2];
-  let aw = a[3];
-  out[0] = ax + t * (b[0] - ax);
-  out[1] = ay + t * (b[1] - ay);
-  out[2] = az + t * (b[2] - az);
-  out[3] = aw + t * (b[3] - aw);
-  return out;
-}
-
-/**
- * Generates a random vector with the given scale
- *
- * @param {vec4} out the receiving vector
- * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
- * @returns {vec4} out
- */
-function random(out, scale) {
-  scale = scale || 1.0;
-
-  // Marsaglia, George. Choosing a Point from the Surface of a
-  // Sphere. Ann. Math. Statist. 43 (1972), no. 2, 645--646.
-  // http://projecteuclid.org/euclid.aoms/1177692644;
-  var v1, v2, v3, v4;
-  var s1, s2;
-  do {
-    v1 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2 - 1;
-    v2 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2 - 1;
-    s1 = v1 * v1 + v2 * v2;
-  } while (s1 >= 1);
-  do {
-    v3 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2 - 1;
-    v4 = _common_js__WEBPACK_IMPORTED_MODULE_0__["RANDOM"]() * 2 - 1;
-    s2 = v3 * v3 + v4 * v4;
-  } while (s2 >= 1);
-
-  var d = Math.sqrt((1 - s1) / s2);
-  out[0] = scale * v1;
-  out[1] = scale * v2;
-  out[2] = scale * v3 * d;
-  out[3] = scale * v4 * d;
-  return out;
-}
-
-/**
- * Transforms the vec4 with a mat4.
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the vector to transform
- * @param {mat4} m matrix to transform with
- * @returns {vec4} out
- */
-function transformMat4(out, a, m) {
-  let x = a[0], y = a[1], z = a[2], w = a[3];
-  out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
-  out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
-  out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
-  out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
-  return out;
-}
-
-/**
- * Transforms the vec4 with a quat
- *
- * @param {vec4} out the receiving vector
- * @param {vec4} a the vector to transform
- * @param {quat} q quaternion to transform with
- * @returns {vec4} out
- */
-function transformQuat(out, a, q) {
-  let x = a[0], y = a[1], z = a[2];
-  let qx = q[0], qy = q[1], qz = q[2], qw = q[3];
-
-  // calculate quat * vec
-  let ix = qw * x + qy * z - qz * y;
-  let iy = qw * y + qz * x - qx * z;
-  let iz = qw * z + qx * y - qy * x;
-  let iw = -qx * x - qy * y - qz * z;
-
-  // calculate result * inverse quat
-  out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
-  out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
-  out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
-  out[3] = a[3];
-  return out;
-}
-
-/**
- * Returns a string representation of a vector
- *
- * @param {vec4} a vector to represent as a string
- * @returns {String} string representation of the vector
- */
-function str(a) {
-  return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
-}
-
-/**
- * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
- *
- * @param {vec4} a The first vector.
- * @param {vec4} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function exactEquals(a, b) {
-  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
-}
-
-/**
- * Returns whether or not the vectors have approximately the same elements in the same position.
- *
- * @param {vec4} a The first vector.
- * @param {vec4} b The second vector.
- * @returns {Boolean} True if the vectors are equal, false otherwise.
- */
-function equals(a, b) {
-  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
-  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
-  return (Math.abs(a0 - b0) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
-          Math.abs(a1 - b1) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
-          Math.abs(a2 - b2) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
-          Math.abs(a3 - b3) <= _common_js__WEBPACK_IMPORTED_MODULE_0__["EPSILON"]*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
-}
-
-/**
- * Alias for {@link vec4.subtract}
- * @function
- */
-const sub = subtract;
-
-/**
- * Alias for {@link vec4.multiply}
- * @function
- */
-const mul = multiply;
-
-/**
- * Alias for {@link vec4.divide}
- * @function
- */
-const div = divide;
-
-/**
- * Alias for {@link vec4.distance}
- * @function
- */
-const dist = distance;
-
-/**
- * Alias for {@link vec4.squaredDistance}
- * @function
- */
-const sqrDist = squaredDistance;
-
-/**
- * Alias for {@link vec4.length}
- * @function
- */
-const len = length;
-
-/**
- * Alias for {@link vec4.squaredLength}
- * @function
- */
-const sqrLen = squaredLength;
-
-/**
- * Perform some operation over an array of vec4s.
- *
- * @param {Array} a the array of vectors to iterate over
- * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed
- * @param {Number} offset Number of elements to skip at the beginning of the array
- * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array
- * @param {Function} fn Function to call for each vector in the array
- * @param {Object} [arg] additional argument to pass to fn
- * @returns {Array} a
- * @function
- */
-const forEach = (function() {
-  let vec = create();
-
-  return function(a, stride, offset, count, fn, arg) {
-    let i, l;
-    if(!stride) {
-      stride = 4;
-    }
-
-    if(!offset) {
-      offset = 0;
-    }
-
-    if(count) {
-      l = Math.min((count * stride) + offset, a.length);
-    } else {
-      l = a.length;
-    }
-
-    for(i = offset; i < l; i += stride) {
-      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3];
-      fn(vec, vec, arg);
-      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3];
-    }
-
-    return a;
-  };
-})();
-
-
-/***/ }),
-
-/***/ "./src/core/material.js":
-/*!******************************!*\
-  !*** ./src/core/material.js ***!
-  \******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-exports.stateToBlendFunc = stateToBlendFunc;
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var GL = WebGLRenderingContext; // For enums
-
-var CAP = exports.CAP = {
-  // Enable caps
-  CULL_FACE: 0x001,
-  BLEND: 0x002,
-  DEPTH_TEST: 0x004,
-  STENCIL_TEST: 0x008,
-  COLOR_MASK: 0x010,
-  DEPTH_MASK: 0x020,
-  STENCIL_MASK: 0x040
-};
-
-var MAT_STATE = exports.MAT_STATE = {
-  CAPS_RANGE: 0x000000FF,
-  BLEND_SRC_SHIFT: 8,
-  BLEND_SRC_RANGE: 0x00000F00,
-  BLEND_DST_SHIFT: 12,
-  BLEND_DST_RANGE: 0x0000F000,
-  BLEND_FUNC_RANGE: 0x0000FF00,
-  DEPTH_FUNC_SHIFT: 16,
-  DEPTH_FUNC_RANGE: 0x000F0000
-};
-
-var RENDER_ORDER = exports.RENDER_ORDER = {
-  // Render opaque objects first.
-  OPAQUE: 0,
-
-  // Render the sky after all opaque object to save fill rate.
-  SKY: 1,
-
-  // Render transparent objects next so that the opaqe objects show through.
-  TRANSPARENT: 2,
-
-  // Finally render purely additive effects like pointer rays so that they
-  // can render without depth mask.
-  ADDITIVE: 3,
-
-  // Render order will be picked based on the material properties.
-  DEFAULT: 4
-};
-
-function stateToBlendFunc(state, mask, shift) {
-  var value = (state & mask) >> shift;
-  switch (value) {
-    case 0:
-    case 1:
-      return value;
-    default:
-      return value - 2 + GL.SRC_COLOR;
-  }
-}
-
-var MaterialState = exports.MaterialState = function () {
-  function MaterialState() {
-    _classCallCheck(this, MaterialState);
-
-    this._state = CAP.CULL_FACE | CAP.DEPTH_TEST | CAP.COLOR_MASK | CAP.DEPTH_MASK;
-
-    // Use a fairly commonly desired blend func as the default.
-    this.blendFuncSrc = GL.SRC_ALPHA;
-    this.blendFuncDst = GL.ONE_MINUS_SRC_ALPHA;
-
-    this.depthFunc = GL.LESS;
-  }
-
-  _createClass(MaterialState, [{
-    key: "cullFace",
-    get: function get() {
-      return !!(this._state & CAP.CULL_FACE);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.CULL_FACE;
-      } else {
-        this._state &= ~CAP.CULL_FACE;
-      }
-    }
-  }, {
-    key: "blend",
-    get: function get() {
-      return !!(this._state & CAP.BLEND);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.BLEND;
-      } else {
-        this._state &= ~CAP.BLEND;
-      }
-    }
-  }, {
-    key: "depthTest",
-    get: function get() {
-      return !!(this._state & CAP.DEPTH_TEST);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.DEPTH_TEST;
-      } else {
-        this._state &= ~CAP.DEPTH_TEST;
-      }
-    }
-  }, {
-    key: "stencilTest",
-    get: function get() {
-      return !!(this._state & CAP.STENCIL_TEST);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.STENCIL_TEST;
-      } else {
-        this._state &= ~CAP.STENCIL_TEST;
-      }
-    }
-  }, {
-    key: "colorMask",
-    get: function get() {
-      return !!(this._state & CAP.COLOR_MASK);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.COLOR_MASK;
-      } else {
-        this._state &= ~CAP.COLOR_MASK;
-      }
-    }
-  }, {
-    key: "depthMask",
-    get: function get() {
-      return !!(this._state & CAP.DEPTH_MASK);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.DEPTH_MASK;
-      } else {
-        this._state &= ~CAP.DEPTH_MASK;
-      }
-    }
-  }, {
-    key: "depthFunc",
-    get: function get() {
-      return ((this._state & MAT_STATE.DEPTH_FUNC_RANGE) >> MAT_STATE.DEPTH_FUNC_SHIFT) + GL.NEVER;
-    },
-    set: function set(value) {
-      value = value - GL.NEVER;
-      this._state &= ~MAT_STATE.DEPTH_FUNC_RANGE;
-      this._state |= value << MAT_STATE.DEPTH_FUNC_SHIFT;
-    }
-  }, {
-    key: "stencilMask",
-    get: function get() {
-      return !!(this._state & CAP.STENCIL_MASK);
-    },
-    set: function set(value) {
-      if (value) {
-        this._state |= CAP.STENCIL_MASK;
-      } else {
-        this._state &= ~CAP.STENCIL_MASK;
-      }
-    }
-  }, {
-    key: "blendFuncSrc",
-    get: function get() {
-      return stateToBlendFunc(this._state, MAT_STATE.BLEND_SRC_RANGE, MAT_STATE.BLEND_SRC_SHIFT);
-    },
-    set: function set(value) {
-      switch (value) {
-        case 0:
-        case 1:
-          break;
-        default:
-          value = value - GL.SRC_COLOR + 2;
-      }
-      this._state &= ~MAT_STATE.BLEND_SRC_RANGE;
-      this._state |= value << MAT_STATE.BLEND_SRC_SHIFT;
-    }
-  }, {
-    key: "blendFuncDst",
-    get: function get() {
-      return stateToBlendFunc(this._state, MAT_STATE.BLEND_DST_RANGE, MAT_STATE.BLEND_DST_SHIFT);
-    },
-    set: function set(value) {
-      switch (value) {
-        case 0:
-        case 1:
-          break;
-        default:
-          value = value - GL.SRC_COLOR + 2;
-      }
-      this._state &= ~MAT_STATE.BLEND_DST_RANGE;
-      this._state |= value << MAT_STATE.BLEND_DST_SHIFT;
-    }
-  }]);
-
-  return MaterialState;
-}();
-
-var MaterialSampler = function () {
-  function MaterialSampler(uniformName) {
-    _classCallCheck(this, MaterialSampler);
-
-    this._uniformName = uniformName;
-    this._texture = null;
-  }
-
-  _createClass(MaterialSampler, [{
-    key: "texture",
-    get: function get() {
-      return this._texture;
-    },
-    set: function set(value) {
-      this._texture = value;
-    }
-  }]);
-
-  return MaterialSampler;
-}();
-
-var MaterialUniform = function () {
-  function MaterialUniform(uniformName, defaultValue, length) {
-    _classCallCheck(this, MaterialUniform);
-
-    this._uniformName = uniformName;
-    this._value = defaultValue;
-    this._length = length;
-    if (!this._length) {
-      if (defaultValue instanceof Array) {
-        this._length = defaultValue.length;
-      } else {
-        this._length = 1;
-      }
-    }
-  }
-
-  _createClass(MaterialUniform, [{
-    key: "value",
-    get: function get() {
-      return this._value;
-    },
-    set: function set(value) {
-      this._value = value;
-    }
-  }]);
-
-  return MaterialUniform;
-}();
-
-var Material = exports.Material = function () {
-  function Material() {
-    _classCallCheck(this, Material);
-
-    this.state = new MaterialState();
-    this.renderOrder = RENDER_ORDER.DEFAULT;
-    this._samplers = [];
-    this._uniforms = [];
-  }
-
-  _createClass(Material, [{
-    key: "defineSampler",
-    value: function defineSampler(uniformName) {
-      var sampler = new MaterialSampler(uniformName);
-      this._samplers.push(sampler);
-      return sampler;
-    }
-  }, {
-    key: "defineUniform",
-    value: function defineUniform(uniformName) {
-      var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
-      var length = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
-
-      var uniform = new MaterialUniform(uniformName, defaultValue, length);
-      this._uniforms.push(uniform);
-      return uniform;
-    }
-  }, {
-    key: "getProgramDefines",
-    value: function getProgramDefines(renderPrimitive) {
-      return {};
-    }
-  }, {
-    key: "materialName",
-    get: function get() {
-      return null;
-    }
-  }, {
-    key: "vertexSource",
-    get: function get() {
-      return null;
-    }
-  }, {
-    key: "fragmentSource",
-    get: function get() {
-      return null;
-    }
-  }]);
-
-  return Material;
-}();
-
-/***/ }),
-
-/***/ "./src/core/node.js":
-/*!**************************!*\
-  !*** ./src/core/node.js ***!
-  \**************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Node = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var _ray = __webpack_require__(/*! ../math/ray.js */ "./src/math/ray.js");
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var DEFAULT_TRANSLATION = new Float32Array([0, 0, 0]);
-var DEFAULT_ROTATION = new Float32Array([0, 0, 0, 1]);
-var DEFAULT_SCALE = new Float32Array([1, 1, 1]);
-
-var tmpRayMatrix = _glMatrix.mat4.create();
-
-var Node = exports.Node = function () {
-  function Node() {
-    _classCallCheck(this, Node);
-
-    this.name = null; // Only for debugging
-    this.children = [];
-    this.parent = null;
-    this.visible = true;
-    this.selectable = false;
-
-    this._matrix = null;
-
-    this._dirtyTRS = false;
-    this._translation = null;
-    this._rotation = null;
-    this._scale = null;
-
-    this._dirtyWorldMatrix = false;
-    this._worldMatrix = null;
-
-    this._activeFrameId = -1;
-    this._hoverFrameId = -1;
-    this._renderPrimitives = null;
-    this._renderer = null;
-
-    this._selectHandler = null;
-  }
-
-  _createClass(Node, [{
-    key: '_setRenderer',
-    value: function _setRenderer(renderer) {
-      if (this._renderer == renderer) {
-        return;
-      }
-
-      if (this._renderer) {
-        // Changing the renderer removes any previously attached renderPrimitives
-        // from a different renderer.
-        this.clearRenderPrimitives();
-      }
-
-      this._renderer = renderer;
-      if (renderer) {
-        this.onRendererChanged(renderer);
-
-        var _iteratorNormalCompletion = true;
-        var _didIteratorError = false;
-        var _iteratorError = undefined;
-
-        try {
-          for (var _iterator = this.children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-            var child = _step.value;
-
-            child._setRenderer(renderer);
-          }
-        } catch (err) {
-          _didIteratorError = true;
-          _iteratorError = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion && _iterator.return) {
-              _iterator.return();
-            }
-          } finally {
-            if (_didIteratorError) {
-              throw _iteratorError;
-            }
-          }
-        }
-      }
-    }
-  }, {
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {}
-    // Override in other node types to respond to changes in the renderer.
-
-
-    // Create a clone of this node and all of it's children. Does not duplicate
-    // RenderPrimitives, the cloned nodes will be treated as new instances of the
-    // geometry.
-
-  }, {
-    key: 'clone',
-    value: function clone() {
-      var _this = this;
-
-      var cloneNode = new Node();
-      cloneNode.name = this.name;
-      cloneNode.visible = this.visible;
-      cloneNode._renderer = this._renderer;
-
-      cloneNode._dirtyTRS = this._dirtyTRS;
-
-      if (this._translation) {
-        cloneNode._translation = _glMatrix.vec3.create();
-        _glMatrix.vec3.copy(cloneNode._translation, this._translation);
-      }
-
-      if (this._rotation) {
-        cloneNode._rotation = _glMatrix.quat.create();
-        _glMatrix.quat.copy(cloneNode._rotation, this._rotation);
-      }
-
-      if (this._scale) {
-        cloneNode._scale = _glMatrix.vec3.create();
-        _glMatrix.vec3.copy(cloneNode._scale, this._scale);
-      }
-
-      // Only copy the matrices if they're not already dirty.
-      if (!cloneNode._dirtyTRS && this._matrix) {
-        cloneNode._matrix = _glMatrix.mat4.create();
-        _glMatrix.mat4.copy(cloneNode._matrix, this._matrix);
-      }
-
-      cloneNode._dirtyWorldMatrix = this._dirtyWorldMatrix;
-      if (!cloneNode._dirtyWorldMatrix && this._worldMatrix) {
-        cloneNode._worldMatrix = _glMatrix.mat4.create();
-        _glMatrix.mat4.copy(cloneNode._worldMatrix, this._worldMatrix);
-      }
-
-      this.waitForComplete().then(function () {
-        if (_this._renderPrimitives) {
-          var _iteratorNormalCompletion2 = true;
-          var _didIteratorError2 = false;
-          var _iteratorError2 = undefined;
-
-          try {
-            for (var _iterator2 = _this._renderPrimitives[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-              var primitive = _step2.value;
-
-              cloneNode.addRenderPrimitive(primitive);
-            }
-          } catch (err) {
-            _didIteratorError2 = true;
-            _iteratorError2 = err;
-          } finally {
-            try {
-              if (!_iteratorNormalCompletion2 && _iterator2.return) {
-                _iterator2.return();
-              }
-            } finally {
-              if (_didIteratorError2) {
-                throw _iteratorError2;
-              }
-            }
-          }
-        }
-
-        var _iteratorNormalCompletion3 = true;
-        var _didIteratorError3 = false;
-        var _iteratorError3 = undefined;
-
-        try {
-          for (var _iterator3 = _this.children[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
-            var child = _step3.value;
-
-            cloneNode.addNode(child.clone());
-          }
-        } catch (err) {
-          _didIteratorError3 = true;
-          _iteratorError3 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion3 && _iterator3.return) {
-              _iterator3.return();
-            }
-          } finally {
-            if (_didIteratorError3) {
-              throw _iteratorError3;
-            }
-          }
-        }
-      });
-
-      return cloneNode;
-    }
-  }, {
-    key: 'markActive',
-    value: function markActive(frameId) {
-      if (this.visible && this._renderPrimitives) {
-        this._activeFrameId = frameId;
-        var _iteratorNormalCompletion4 = true;
-        var _didIteratorError4 = false;
-        var _iteratorError4 = undefined;
-
-        try {
-          for (var _iterator4 = this._renderPrimitives[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
-            var primitive = _step4.value;
-
-            primitive.markActive(frameId);
-          }
-        } catch (err) {
-          _didIteratorError4 = true;
-          _iteratorError4 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion4 && _iterator4.return) {
-              _iterator4.return();
-            }
-          } finally {
-            if (_didIteratorError4) {
-              throw _iteratorError4;
-            }
-          }
-        }
-      }
-
-      var _iteratorNormalCompletion5 = true;
-      var _didIteratorError5 = false;
-      var _iteratorError5 = undefined;
-
-      try {
-        for (var _iterator5 = this.children[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
-          var child = _step5.value;
-
-          if (child.visible) {
-            child.markActive(frameId);
-          }
-        }
-      } catch (err) {
-        _didIteratorError5 = true;
-        _iteratorError5 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion5 && _iterator5.return) {
-            _iterator5.return();
-          }
-        } finally {
-          if (_didIteratorError5) {
-            throw _iteratorError5;
-          }
-        }
-      }
-    }
-  }, {
-    key: 'addNode',
-    value: function addNode(value) {
-      if (!value || value.parent == this) {
-        return;
-      }
-
-      if (value.parent) {
-        value.parent.removeNode(value);
-      }
-      value.parent = this;
-
-      this.children.push(value);
-
-      if (this._renderer) {
-        value._setRenderer(this._renderer);
-      }
-    }
-  }, {
-    key: 'removeNode',
-    value: function removeNode(value) {
-      var i = this.children.indexOf(value);
-      if (i > -1) {
-        this.children.splice(i, 1);
-        value.parent = null;
-      }
-    }
-  }, {
-    key: 'clearNodes',
-    value: function clearNodes() {
-      var _iteratorNormalCompletion6 = true;
-      var _didIteratorError6 = false;
-      var _iteratorError6 = undefined;
-
-      try {
-        for (var _iterator6 = this.children[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
-          var child = _step6.value;
-
-          child.parent = null;
-        }
-      } catch (err) {
-        _didIteratorError6 = true;
-        _iteratorError6 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion6 && _iterator6.return) {
-            _iterator6.return();
-          }
-        } finally {
-          if (_didIteratorError6) {
-            throw _iteratorError6;
-          }
-        }
-      }
-
-      this.children = [];
-    }
-  }, {
-    key: 'setMatrixDirty',
-    value: function setMatrixDirty() {
-      if (!this._dirtyWorldMatrix) {
-        this._dirtyWorldMatrix = true;
-        var _iteratorNormalCompletion7 = true;
-        var _didIteratorError7 = false;
-        var _iteratorError7 = undefined;
-
-        try {
-          for (var _iterator7 = this.children[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
-            var child = _step7.value;
-
-            child.setMatrixDirty();
-          }
-        } catch (err) {
-          _didIteratorError7 = true;
-          _iteratorError7 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion7 && _iterator7.return) {
-              _iterator7.return();
-            }
-          } finally {
-            if (_didIteratorError7) {
-              throw _iteratorError7;
-            }
-          }
-        }
-      }
-    }
-  }, {
-    key: '_updateLocalMatrix',
-    value: function _updateLocalMatrix() {
-      if (!this._matrix) {
-        this._matrix = _glMatrix.mat4.create();
-      }
-
-      if (this._dirtyTRS) {
-        this._dirtyTRS = false;
-        _glMatrix.mat4.fromRotationTranslationScale(this._matrix, this._rotation || DEFAULT_ROTATION, this._translation || DEFAULT_TRANSLATION, this._scale || DEFAULT_SCALE);
-      }
-
-      return this._matrix;
-    }
-  }, {
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      var _this2 = this;
-
-      var childPromises = [];
-      var _iteratorNormalCompletion8 = true;
-      var _didIteratorError8 = false;
-      var _iteratorError8 = undefined;
-
-      try {
-        for (var _iterator8 = this.children[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
-          var child = _step8.value;
-
-          childPromises.push(child.waitForComplete());
-        }
-      } catch (err) {
-        _didIteratorError8 = true;
-        _iteratorError8 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion8 && _iterator8.return) {
-            _iterator8.return();
-          }
-        } finally {
-          if (_didIteratorError8) {
-            throw _iteratorError8;
-          }
-        }
-      }
-
-      if (this._renderPrimitives) {
-        var _iteratorNormalCompletion9 = true;
-        var _didIteratorError9 = false;
-        var _iteratorError9 = undefined;
-
-        try {
-          for (var _iterator9 = this._renderPrimitives[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
-            var primitive = _step9.value;
-
-            childPromises.push(primitive.waitForComplete());
-          }
-        } catch (err) {
-          _didIteratorError9 = true;
-          _iteratorError9 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion9 && _iterator9.return) {
-              _iterator9.return();
-            }
-          } finally {
-            if (_didIteratorError9) {
-              throw _iteratorError9;
-            }
-          }
-        }
-      }
-      return Promise.all(childPromises).then(function () {
-        return _this2;
-      });
-    }
-  }, {
-    key: 'addRenderPrimitive',
-    value: function addRenderPrimitive(primitive) {
-      if (!this._renderPrimitives) {
-        this._renderPrimitives = [primitive];
-      } else {
-        this._renderPrimitives.push(primitive);
-      }
-      primitive._instances.push(this);
-    }
-  }, {
-    key: 'removeRenderPrimitive',
-    value: function removeRenderPrimitive(primitive) {
-      if (!this._renderPrimitives) {
-        return;
-      }
-
-      var index = this._renderPrimitives._instances.indexOf(primitive);
-      if (index > -1) {
-        this._renderPrimitives._instances.splice(index, 1);
-
-        index = primitive._instances.indexOf(this);
-        if (index > -1) {
-          primitive._instances.splice(index, 1);
-        }
-
-        if (!this._renderPrimitives.length) {
-          this._renderPrimitives = null;
-        }
-      }
-    }
-  }, {
-    key: 'clearRenderPrimitives',
-    value: function clearRenderPrimitives() {
-      if (this._renderPrimitives) {
-        var _iteratorNormalCompletion10 = true;
-        var _didIteratorError10 = false;
-        var _iteratorError10 = undefined;
-
-        try {
-          for (var _iterator10 = this._renderPrimitives[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
-            var primitive = _step10.value;
-
-            var index = primitive._instances.indexOf(this);
-            if (index > -1) {
-              primitive._instances.splice(index, 1);
-            }
-          }
-        } catch (err) {
-          _didIteratorError10 = true;
-          _iteratorError10 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion10 && _iterator10.return) {
-              _iterator10.return();
-            }
-          } finally {
-            if (_didIteratorError10) {
-              throw _iteratorError10;
-            }
-          }
-        }
-
-        this._renderPrimitives = null;
-      }
-    }
-  }, {
-    key: '_hitTestSelectableNode',
-    value: function _hitTestSelectableNode(ray) {
-      if (this._renderPrimitives) {
-        var localRay = null;
-        var _iteratorNormalCompletion11 = true;
-        var _didIteratorError11 = false;
-        var _iteratorError11 = undefined;
-
-        try {
-          for (var _iterator11 = this._renderPrimitives[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
-            var primitive = _step11.value;
-
-            if (primitive._min) {
-              if (!localRay) {
-                _glMatrix.mat4.invert(tmpRayMatrix, this.worldMatrix);
-                _glMatrix.mat4.multiply(tmpRayMatrix, tmpRayMatrix, ray.transformMatrix);
-                localRay = new _ray.Ray(tmpRayMatrix);
-              }
-              var intersection = localRay.intersectsAABB(primitive._min, primitive._max);
-              if (intersection) {
-                _glMatrix.vec3.transformMat4(intersection, intersection, this.worldMatrix);
-                return intersection;
-              }
-            }
-          }
-        } catch (err) {
-          _didIteratorError11 = true;
-          _iteratorError11 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion11 && _iterator11.return) {
-              _iterator11.return();
-            }
-          } finally {
-            if (_didIteratorError11) {
-              throw _iteratorError11;
-            }
-          }
-        }
-      }
-      var _iteratorNormalCompletion12 = true;
-      var _didIteratorError12 = false;
-      var _iteratorError12 = undefined;
-
-      try {
-        for (var _iterator12 = this.children[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
-          var child = _step12.value;
-
-          var _intersection = child._hitTestSelectableNode(ray);
-          if (_intersection) {
-            return _intersection;
-          }
-        }
-      } catch (err) {
-        _didIteratorError12 = true;
-        _iteratorError12 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion12 && _iterator12.return) {
-            _iterator12.return();
-          }
-        } finally {
-          if (_didIteratorError12) {
-            throw _iteratorError12;
-          }
-        }
-      }
-
-      return null;
-    }
-  }, {
-    key: 'hitTest',
-    value: function hitTest(ray) {
-      if (this.selectable && this.visible) {
-        var intersection = this._hitTestSelectableNode(ray);
-
-        if (intersection) {
-          var origin = _glMatrix.vec3.fromValues(ray.origin.x, ray.origin.y, ray.origin.z);
-          return {
-            node: this,
-            intersection: intersection,
-            distance: _glMatrix.vec3.distance(origin, intersection)
-          };
-        }
-        return null;
-      }
-
-      var result = null;
-      var _iteratorNormalCompletion13 = true;
-      var _didIteratorError13 = false;
-      var _iteratorError13 = undefined;
-
-      try {
-        for (var _iterator13 = this.children[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
-          var child = _step13.value;
-
-          var childResult = child.hitTest(ray);
-          if (childResult) {
-            if (!result || result.distance > childResult.distance) {
-              result = childResult;
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError13 = true;
-        _iteratorError13 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion13 && _iterator13.return) {
-            _iterator13.return();
-          }
-        } finally {
-          if (_didIteratorError13) {
-            throw _iteratorError13;
-          }
-        }
-      }
-
-      return result;
-    }
-  }, {
-    key: 'onSelect',
-    value: function onSelect(value) {
-      this._selectHandler = value;
-    }
-  }, {
-    key: 'handleSelect',
-
-
-    // Called when a selectable node is selected.
-    value: function handleSelect() {
-      if (this._selectHandler) {
-        this._selectHandler();
-      }
-    }
-
-    // Called when a selectable element is pointed at.
-
-  }, {
-    key: 'onHoverStart',
-    value: function onHoverStart() {}
-
-    // Called when a selectable element is no longer pointed at.
-
-  }, {
-    key: 'onHoverEnd',
-    value: function onHoverEnd() {}
-  }, {
-    key: '_update',
-    value: function _update(timestamp, frameDelta) {
-      this.onUpdate(timestamp, frameDelta);
-
-      var _iteratorNormalCompletion14 = true;
-      var _didIteratorError14 = false;
-      var _iteratorError14 = undefined;
-
-      try {
-        for (var _iterator14 = this.children[Symbol.iterator](), _step14; !(_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done); _iteratorNormalCompletion14 = true) {
-          var child = _step14.value;
-
-          child._update(timestamp, frameDelta);
-        }
-      } catch (err) {
-        _didIteratorError14 = true;
-        _iteratorError14 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion14 && _iterator14.return) {
-            _iterator14.return();
-          }
-        } finally {
-          if (_didIteratorError14) {
-            throw _iteratorError14;
-          }
-        }
-      }
-    }
-
-    // Called every frame so that the nodes can animate themselves
-
-  }, {
-    key: 'onUpdate',
-    value: function onUpdate(timestamp, frameDelta) {}
-  }, {
-    key: 'matrix',
-    set: function set(value) {
-      if (value) {
-        if (!this._matrix) {
-          this._matrix = _glMatrix.mat4.create();
-        }
-        _glMatrix.mat4.copy(this._matrix, value);
-      } else {
-        this._matrix = null;
-      }
-      this.setMatrixDirty();
-      this._dirtyTRS = false;
-      this._translation = null;
-      this._rotation = null;
-      this._scale = null;
-    },
-    get: function get() {
-      this.setMatrixDirty();
-
-      return this._updateLocalMatrix();
-    }
-  }, {
-    key: 'worldMatrix',
-    get: function get() {
-      if (!this._worldMatrix) {
-        this._dirtyWorldMatrix = true;
-        this._worldMatrix = _glMatrix.mat4.create();
-      }
-
-      if (this._dirtyWorldMatrix || this._dirtyTRS) {
-        if (this.parent) {
-          // TODO: Some optimizations that could be done here if the node matrix
-          // is an identity matrix.
-          _glMatrix.mat4.mul(this._worldMatrix, this.parent.worldMatrix, this._updateLocalMatrix());
-        } else {
-          _glMatrix.mat4.copy(this._worldMatrix, this._updateLocalMatrix());
-        }
-        this._dirtyWorldMatrix = false;
-      }
-
-      return this._worldMatrix;
-    }
-
-    // TODO: Decompose matrix when fetching these?
-
-  }, {
-    key: 'translation',
-    set: function set(value) {
-      if (value != null) {
-        this._dirtyTRS = true;
-        this.setMatrixDirty();
-      }
-      this._translation = value;
-    },
-    get: function get() {
-      this._dirtyTRS = true;
-      this.setMatrixDirty();
-      if (!this._translation) {
-        this._translation = _glMatrix.vec3.clone(DEFAULT_TRANSLATION);
-      }
-      return this._translation;
-    }
-  }, {
-    key: 'rotation',
-    set: function set(value) {
-      if (value != null) {
-        this._dirtyTRS = true;
-        this.setMatrixDirty();
-      }
-      this._rotation = value;
-    },
-    get: function get() {
-      this._dirtyTRS = true;
-      this.setMatrixDirty();
-      if (!this._rotation) {
-        this._rotation = _glMatrix.quat.clone(DEFAULT_ROTATION);
-      }
-      return this._rotation;
-    }
-  }, {
-    key: 'scale',
-    set: function set(value) {
-      if (value != null) {
-        this._dirtyTRS = true;
-        this.setMatrixDirty();
-      }
-      this._scale = value;
-    },
-    get: function get() {
-      this._dirtyTRS = true;
-      this.setMatrixDirty();
-      if (!this._scale) {
-        this._scale = _glMatrix.vec3.clone(DEFAULT_SCALE);
-      }
-      return this._scale;
-    }
-  }, {
-    key: 'renderPrimitives',
-    get: function get() {
-      return this._renderPrimitives;
-    }
-  }, {
-    key: 'selectHandler',
-    get: function get() {
-      return this._selectHandler;
-    }
-  }]);
-
-  return Node;
-}();
-
-/***/ }),
-
-/***/ "./src/core/primitive.js":
-/*!*******************************!*\
-  !*** ./src/core/primitive.js ***!
-  \*******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Primitive = exports.PrimitiveAttribute = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var PrimitiveAttribute = exports.PrimitiveAttribute = function PrimitiveAttribute(name, buffer, componentCount, componentType, stride, byteOffset) {
-  _classCallCheck(this, PrimitiveAttribute);
-
-  this.name = name;
-  this.buffer = buffer;
-  this.componentCount = componentCount || 3;
-  this.componentType = componentType || 5126; // gl.FLOAT;
-  this.stride = stride || 0;
-  this.byteOffset = byteOffset || 0;
-  this.normalized = false;
-};
-
-var Primitive = exports.Primitive = function () {
-  function Primitive(attributes, elementCount, mode) {
-    _classCallCheck(this, Primitive);
-
-    this.attributes = attributes || [];
-    this.elementCount = elementCount || 0;
-    this.mode = mode || 4; // gl.TRIANGLES;
-    this.indexBuffer = null;
-    this.indexByteOffset = 0;
-    this.indexType = 0;
-    this._min = null;
-    this._max = null;
-  }
-
-  _createClass(Primitive, [{
-    key: 'setIndexBuffer',
-    value: function setIndexBuffer(indexBuffer, byteOffset, indexType) {
-      this.indexBuffer = indexBuffer;
-      this.indexByteOffset = byteOffset || 0;
-      this.indexType = indexType || 5123; // gl.UNSIGNED_SHORT;
-    }
-  }, {
-    key: 'setBounds',
-    value: function setBounds(min, max) {
-      this._min = _glMatrix.vec3.clone(min);
-      this._max = _glMatrix.vec3.clone(max);
-    }
-  }]);
-
-  return Primitive;
-}();
-
-/***/ }),
-
-/***/ "./src/core/program.js":
-/*!*****************************!*\
-  !*** ./src/core/program.js ***!
-  \*****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var Program = exports.Program = function () {
-  function Program(gl, vertSrc, fragSrc, attribMap, defines) {
-    _classCallCheck(this, Program);
-
-    this._gl = gl;
-    this.program = gl.createProgram();
-    this.attrib = null;
-    this.uniform = null;
-    this.defines = {};
-
-    this._firstUse = true;
-    this._nextUseCallbacks = [];
-
-    var definesString = '';
-    if (defines) {
-      for (var define in defines) {
-        this.defines[define] = defines[define];
-        definesString += '#define ' + define + ' ' + defines[define] + '\n';
-      }
-    }
-
-    this._vertShader = gl.createShader(gl.VERTEX_SHADER);
-    gl.attachShader(this.program, this._vertShader);
-    gl.shaderSource(this._vertShader, definesString + vertSrc);
-    gl.compileShader(this._vertShader);
-
-    this._fragShader = gl.createShader(gl.FRAGMENT_SHADER);
-    gl.attachShader(this.program, this._fragShader);
-    gl.shaderSource(this._fragShader, definesString + fragSrc);
-    gl.compileShader(this._fragShader);
-
-    if (attribMap) {
-      this.attrib = {};
-      for (var attribName in attribMap) {
-        gl.bindAttribLocation(this.program, attribMap[attribName], attribName);
-        this.attrib[attribName] = attribMap[attribName];
-      }
-    }
-
-    gl.linkProgram(this.program);
-  }
-
-  _createClass(Program, [{
-    key: 'onNextUse',
-    value: function onNextUse(callback) {
-      this._nextUseCallbacks.push(callback);
-    }
-  }, {
-    key: 'use',
-    value: function use() {
-      var gl = this._gl;
-
-      // If this is the first time the program has been used do all the error checking and
-      // attrib/uniform querying needed.
-      if (this._firstUse) {
-        this._firstUse = false;
-        if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
-          if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) {
-            console.error('Vertex shader compile error: ' + gl.getShaderInfoLog(this._vertShader));
-          } else if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) {
-            console.error('Fragment shader compile error: ' + gl.getShaderInfoLog(this._fragShader));
-          } else {
-            console.error('Program link error: ' + gl.getProgramInfoLog(this.program));
-          }
-          gl.deleteProgram(this.program);
-          this.program = null;
-        } else {
-          if (!this.attrib) {
-            this.attrib = {};
-            var attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES);
-            for (var i = 0; i < attribCount; i++) {
-              var attribInfo = gl.getActiveAttrib(this.program, i);
-              this.attrib[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name);
-            }
-          }
-
-          this.uniform = {};
-          var uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
-          var uniformName = '';
-          for (var _i = 0; _i < uniformCount; _i++) {
-            var uniformInfo = gl.getActiveUniform(this.program, _i);
-            uniformName = uniformInfo.name.replace('[0]', '');
-            this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName);
-          }
-        }
-        gl.deleteShader(this._vertShader);
-        gl.deleteShader(this._fragShader);
-      }
-
-      gl.useProgram(this.program);
-
-      if (this._nextUseCallbacks.length) {
-        var _iteratorNormalCompletion = true;
-        var _didIteratorError = false;
-        var _iteratorError = undefined;
-
-        try {
-          for (var _iterator = this._nextUseCallbacks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-            var callback = _step.value;
-
-            callback(this);
-          }
-        } catch (err) {
-          _didIteratorError = true;
-          _iteratorError = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion && _iterator.return) {
-              _iterator.return();
-            }
-          } finally {
-            if (_didIteratorError) {
-              throw _iteratorError;
-            }
-          }
-        }
-
-        this._nextUseCallbacks = [];
-      }
-    }
-  }]);
-
-  return Program;
-}();
-
-/***/ }),
-
-/***/ "./src/core/renderer.js":
-/*!******************************!*\
-  !*** ./src/core/renderer.js ***!
-  \******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Renderer = exports.RenderTexture = exports.RenderView = exports.ATTRIB_MASK = exports.ATTRIB = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-exports.createWebGLContext = createWebGLContext;
-
-var _material = __webpack_require__(/*! ./material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ./node.js */ "./src/core/node.js");
-
-var _program = __webpack_require__(/*! ./program.js */ "./src/core/program.js");
-
-var _texture = __webpack_require__(/*! ./texture.js */ "./src/core/texture.js");
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var ATTRIB = exports.ATTRIB = {
-  POSITION: 1,
-  NORMAL: 2,
-  TANGENT: 3,
-  TEXCOORD_0: 4,
-  TEXCOORD_1: 5,
-  COLOR_0: 6
-};
-
-var ATTRIB_MASK = exports.ATTRIB_MASK = {
-  POSITION: 0x0001,
-  NORMAL: 0x0002,
-  TANGENT: 0x0004,
-  TEXCOORD_0: 0x0008,
-  TEXCOORD_1: 0x0010,
-  COLOR_0: 0x0020
-};
-
-var GL = WebGLRenderingContext; // For enums
-
-var DEF_LIGHT_DIR = new Float32Array([-0.1, -1.0, -0.2]);
-var DEF_LIGHT_COLOR = new Float32Array([3.0, 3.0, 3.0]);
-
-var PRECISION_REGEX = new RegExp('precision (lowp|mediump|highp) float;');
-
-var VERTEX_SHADER_SINGLE_ENTRY = '\nuniform mat4 PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX;\n\nvoid main() {\n  gl_Position = vertex_main(PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX);\n}\n';
-
-var VERTEX_SHADER_MULTI_ENTRY = '\n#ERROR Multiview rendering is not implemented\nvoid main() {\n  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n}\n';
-
-var FRAGMENT_SHADER_ENTRY = '\nvoid main() {\n  gl_FragColor = fragment_main();\n}\n';
-
-function isPowerOfTwo(n) {
-  return (n & n - 1) === 0;
-}
-
-// Creates a WebGL context and initializes it with some common default state.
-function createWebGLContext(glAttribs) {
-  glAttribs = glAttribs || { alpha: false };
-
-  var webglCanvas = document.createElement('canvas');
-  var contextTypes = glAttribs.webgl2 ? ['webgl2'] : ['webgl', 'experimental-webgl'];
-  var context = null;
-
-  var _iteratorNormalCompletion = true;
-  var _didIteratorError = false;
-  var _iteratorError = undefined;
-
-  try {
-    for (var _iterator = contextTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-      var contextType = _step.value;
-
-      context = webglCanvas.getContext(contextType, glAttribs);
-      if (context) {
-        break;
-      }
-    }
-  } catch (err) {
-    _didIteratorError = true;
-    _iteratorError = err;
-  } finally {
-    try {
-      if (!_iteratorNormalCompletion && _iterator.return) {
-        _iterator.return();
-      }
-    } finally {
-      if (_didIteratorError) {
-        throw _iteratorError;
-      }
-    }
-  }
-
-  if (!context) {
-    var webglType = glAttribs.webgl2 ? 'WebGL 2' : 'WebGL';
-    console.error('This browser does not support ' + webglType + '.');
-    return null;
-  }
-
-  return context;
-}
-
-var RenderView = exports.RenderView = function () {
-  function RenderView(projectionMatrix, viewMatrix) {
-    var viewport = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
-    var eye = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'left';
-
-    _classCallCheck(this, RenderView);
-
-    this.projectionMatrix = projectionMatrix;
-    this.viewMatrix = viewMatrix;
-    this.viewport = viewport;
-    // If an eye isn't given the left eye is assumed.
-    this._eye = eye;
-    this._eyeIndex = eye == 'left' ? 0 : 1;
-  }
-
-  _createClass(RenderView, [{
-    key: 'eye',
-    get: function get() {
-      return this._eye;
-    },
-    set: function set(value) {
-      this._eye = value;
-      this._eyeIndex = value == 'left' ? 0 : 1;
-    }
-  }, {
-    key: 'eyeIndex',
-    get: function get() {
-      return this._eyeIndex;
-    }
-  }]);
-
-  return RenderView;
-}();
-
-var RenderBuffer = function () {
-  function RenderBuffer(target, usage, buffer) {
-    var _this = this;
-
-    var length = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
-
-    _classCallCheck(this, RenderBuffer);
-
-    this._target = target;
-    this._usage = usage;
-    this._length = length;
-    if (buffer instanceof Promise) {
-      this._buffer = null;
-      this._promise = buffer.then(function (buffer) {
-        _this._buffer = buffer;
-        return _this;
-      });
-    } else {
-      this._buffer = buffer;
-      this._promise = Promise.resolve(this);
-    }
-  }
-
-  _createClass(RenderBuffer, [{
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      return this._promise;
-    }
-  }]);
-
-  return RenderBuffer;
-}();
-
-var RenderPrimitiveAttribute = function RenderPrimitiveAttribute(primitiveAttribute) {
-  _classCallCheck(this, RenderPrimitiveAttribute);
-
-  this._attrib_index = ATTRIB[primitiveAttribute.name];
-  this._componentCount = primitiveAttribute.componentCount;
-  this._componentType = primitiveAttribute.componentType;
-  this._stride = primitiveAttribute.stride;
-  this._byteOffset = primitiveAttribute.byteOffset;
-  this._normalized = primitiveAttribute.normalized;
-};
-
-var RenderPrimitiveAttributeBuffer = function RenderPrimitiveAttributeBuffer(buffer) {
-  _classCallCheck(this, RenderPrimitiveAttributeBuffer);
-
-  this._buffer = buffer;
-  this._attributes = [];
-};
-
-var RenderPrimitive = function () {
-  function RenderPrimitive(primitive) {
-    _classCallCheck(this, RenderPrimitive);
-
-    this._activeFrameId = 0;
-    this._instances = [];
-    this._material = null;
-
-    this.setPrimitive(primitive);
-  }
-
-  _createClass(RenderPrimitive, [{
-    key: 'setPrimitive',
-    value: function setPrimitive(primitive) {
-      this._mode = primitive.mode;
-      this._elementCount = primitive.elementCount;
-      this._promise = null;
-      this._vao = null;
-      this._complete = false;
-      this._attributeBuffers = [];
-      this._attributeMask = 0;
-
-      var _iteratorNormalCompletion2 = true;
-      var _didIteratorError2 = false;
-      var _iteratorError2 = undefined;
-
-      try {
-        for (var _iterator2 = primitive.attributes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-          var attribute = _step2.value;
-
-          this._attributeMask |= ATTRIB_MASK[attribute.name];
-          var renderAttribute = new RenderPrimitiveAttribute(attribute);
-          var foundBuffer = false;
-          var _iteratorNormalCompletion3 = true;
-          var _didIteratorError3 = false;
-          var _iteratorError3 = undefined;
-
-          try {
-            for (var _iterator3 = this._attributeBuffers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
-              var attributeBuffer = _step3.value;
-
-              if (attributeBuffer._buffer == attribute.buffer) {
-                attributeBuffer._attributes.push(renderAttribute);
-                foundBuffer = true;
-                break;
-              }
-            }
-          } catch (err) {
-            _didIteratorError3 = true;
-            _iteratorError3 = err;
-          } finally {
-            try {
-              if (!_iteratorNormalCompletion3 && _iterator3.return) {
-                _iterator3.return();
-              }
-            } finally {
-              if (_didIteratorError3) {
-                throw _iteratorError3;
-              }
-            }
-          }
-
-          if (!foundBuffer) {
-            var _attributeBuffer = new RenderPrimitiveAttributeBuffer(attribute.buffer);
-            _attributeBuffer._attributes.push(renderAttribute);
-            this._attributeBuffers.push(_attributeBuffer);
-          }
-        }
-      } catch (err) {
-        _didIteratorError2 = true;
-        _iteratorError2 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion2 && _iterator2.return) {
-            _iterator2.return();
-          }
-        } finally {
-          if (_didIteratorError2) {
-            throw _iteratorError2;
-          }
-        }
-      }
-
-      this._indexBuffer = null;
-      this._indexByteOffset = 0;
-      this._indexType = 0;
-
-      if (primitive.indexBuffer) {
-        this._indexByteOffset = primitive.indexByteOffset;
-        this._indexType = primitive.indexType;
-        this._indexBuffer = primitive.indexBuffer;
-      }
-
-      if (primitive._min) {
-        this._min = _glMatrix.vec3.clone(primitive._min);
-        this._max = _glMatrix.vec3.clone(primitive._max);
-      } else {
-        this._min = null;
-        this._max = null;
-      }
-
-      if (this._material != null) {
-        this.waitForComplete(); // To flip the _complete flag.
-      }
-    }
-  }, {
-    key: 'setRenderMaterial',
-    value: function setRenderMaterial(material) {
-      this._material = material;
-      this._promise = null;
-      this._complete = false;
-
-      if (this._material != null) {
-        this.waitForComplete(); // To flip the _complete flag.
-      }
-    }
-  }, {
-    key: 'markActive',
-    value: function markActive(frameId) {
-      if (this._complete && this._activeFrameId != frameId) {
-        if (this._material) {
-          if (!this._material.markActive(frameId)) {
-            return;
-          }
-        }
-        this._activeFrameId = frameId;
-      }
-    }
-  }, {
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      var _this2 = this;
-
-      if (!this._promise) {
-        if (!this._material) {
-          return Promise.reject('RenderPrimitive does not have a material');
-        }
-
-        var completionPromises = [];
-
-        var _iteratorNormalCompletion4 = true;
-        var _didIteratorError4 = false;
-        var _iteratorError4 = undefined;
-
-        try {
-          for (var _iterator4 = this._attributeBuffers[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
-            var attributeBuffer = _step4.value;
-
-            if (!attributeBuffer._buffer._buffer) {
-              completionPromises.push(attributeBuffer._buffer._promise);
-            }
-          }
-        } catch (err) {
-          _didIteratorError4 = true;
-          _iteratorError4 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion4 && _iterator4.return) {
-              _iterator4.return();
-            }
-          } finally {
-            if (_didIteratorError4) {
-              throw _iteratorError4;
-            }
-          }
-        }
-
-        if (this._indexBuffer && !this._indexBuffer._buffer) {
-          completionPromises.push(this._indexBuffer._promise);
-        }
-
-        this._promise = Promise.all(completionPromises).then(function () {
-          _this2._complete = true;
-          return _this2;
-        });
-      }
-      return this._promise;
-    }
-  }, {
-    key: 'samplers',
-    get: function get() {
-      return this._material._samplerDictionary;
-    }
-  }, {
-    key: 'uniforms',
-    get: function get() {
-      return this._material._uniform_dictionary;
-    }
-  }]);
-
-  return RenderPrimitive;
-}();
-
-var RenderTexture = exports.RenderTexture = function () {
-  function RenderTexture(texture) {
-    _classCallCheck(this, RenderTexture);
-
-    this._texture = texture;
-    this._complete = false;
-    this._activeFrameId = 0;
-    this._activeCallback = null;
-  }
-
-  _createClass(RenderTexture, [{
-    key: 'markActive',
-    value: function markActive(frameId) {
-      if (this._activeCallback && this._activeFrameId != frameId) {
-        this._activeFrameId = frameId;
-        this._activeCallback(this);
-      }
-    }
-  }]);
-
-  return RenderTexture;
-}();
-
-var inverseMatrix = _glMatrix.mat4.create();
-
-function setCap(gl, glEnum, cap, prevState, state) {
-  var change = (state & cap) - (prevState & cap);
-  if (!change) {
-    return;
-  }
-
-  if (change > 0) {
-    gl.enable(glEnum);
-  } else {
-    gl.disable(glEnum);
-  }
-}
-
-var RenderMaterialSampler = function () {
-  function RenderMaterialSampler(renderer, materialSampler, index) {
-    _classCallCheck(this, RenderMaterialSampler);
-
-    this._renderer = renderer;
-    this._uniformName = materialSampler._uniformName;
-    this._renderTexture = renderer._getRenderTexture(materialSampler._texture);
-    this._index = index;
-  }
-
-  _createClass(RenderMaterialSampler, [{
-    key: 'texture',
-    set: function set(value) {
-      this._renderTexture = this._renderer._getRenderTexture(value);
-    }
-  }]);
-
-  return RenderMaterialSampler;
-}();
-
-var RenderMaterialUniform = function () {
-  function RenderMaterialUniform(materialUniform) {
-    _classCallCheck(this, RenderMaterialUniform);
-
-    this._uniformName = materialUniform._uniformName;
-    this._uniform = null;
-    this._length = materialUniform._length;
-    if (materialUniform._value instanceof Array) {
-      this._value = new Float32Array(materialUniform._value);
-    } else {
-      this._value = new Float32Array([materialUniform._value]);
-    }
-  }
-
-  _createClass(RenderMaterialUniform, [{
-    key: 'value',
-    set: function set(value) {
-      if (this._value.length == 1) {
-        this._value[0] = value;
-      } else {
-        for (var i = 0; i < this._value.length; ++i) {
-          this._value[i] = value[i];
-        }
-      }
-    }
-  }]);
-
-  return RenderMaterialUniform;
-}();
-
-var RenderMaterial = function () {
-  function RenderMaterial(renderer, material, program) {
-    _classCallCheck(this, RenderMaterial);
-
-    this._program = program;
-    this._state = material.state._state;
-    this._activeFrameId = 0;
-    this._completeForActiveFrame = false;
-
-    this._samplerDictionary = {};
-    this._samplers = [];
-    for (var i = 0; i < material._samplers.length; ++i) {
-      var renderSampler = new RenderMaterialSampler(renderer, material._samplers[i], i);
-      this._samplers.push(renderSampler);
-      this._samplerDictionary[renderSampler._uniformName] = renderSampler;
-    }
-
-    this._uniform_dictionary = {};
-    this._uniforms = [];
-    var _iteratorNormalCompletion5 = true;
-    var _didIteratorError5 = false;
-    var _iteratorError5 = undefined;
-
-    try {
-      for (var _iterator5 = material._uniforms[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
-        var uniform = _step5.value;
-
-        var renderUniform = new RenderMaterialUniform(uniform);
-        this._uniforms.push(renderUniform);
-        this._uniform_dictionary[renderUniform._uniformName] = renderUniform;
-      }
-    } catch (err) {
-      _didIteratorError5 = true;
-      _iteratorError5 = err;
-    } finally {
-      try {
-        if (!_iteratorNormalCompletion5 && _iterator5.return) {
-          _iterator5.return();
-        }
-      } finally {
-        if (_didIteratorError5) {
-          throw _iteratorError5;
-        }
-      }
-    }
-
-    this._firstBind = true;
-
-    this._renderOrder = material.renderOrder;
-    if (this._renderOrder == _material.RENDER_ORDER.DEFAULT) {
-      if (this._state & _material.CAP.BLEND) {
-        this._renderOrder = _material.RENDER_ORDER.TRANSPARENT;
-      } else {
-        this._renderOrder = _material.RENDER_ORDER.OPAQUE;
-      }
-    }
-  }
-
-  _createClass(RenderMaterial, [{
-    key: 'bind',
-    value: function bind(gl) {
-      // First time we do a binding, cache the uniform locations and remove
-      // unused uniforms from the list.
-      if (this._firstBind) {
-        for (var i = 0; i < this._samplers.length;) {
-          var sampler = this._samplers[i];
-          if (!this._program.uniform[sampler._uniformName]) {
-            this._samplers.splice(i, 1);
-            continue;
-          }
-          ++i;
-        }
-
-        for (var _i = 0; _i < this._uniforms.length;) {
-          var uniform = this._uniforms[_i];
-          uniform._uniform = this._program.uniform[uniform._uniformName];
-          if (!uniform._uniform) {
-            this._uniforms.splice(_i, 1);
-            continue;
-          }
-          ++_i;
-        }
-        this._firstBind = false;
-      }
-
-      var _iteratorNormalCompletion6 = true;
-      var _didIteratorError6 = false;
-      var _iteratorError6 = undefined;
-
-      try {
-        for (var _iterator6 = this._samplers[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
-          var _sampler = _step6.value;
-
-          gl.activeTexture(gl.TEXTURE0 + _sampler._index);
-          if (_sampler._renderTexture && _sampler._renderTexture._complete) {
-            gl.bindTexture(gl.TEXTURE_2D, _sampler._renderTexture._texture);
-          } else {
-            gl.bindTexture(gl.TEXTURE_2D, null);
-          }
-        }
-      } catch (err) {
-        _didIteratorError6 = true;
-        _iteratorError6 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion6 && _iterator6.return) {
-            _iterator6.return();
-          }
-        } finally {
-          if (_didIteratorError6) {
-            throw _iteratorError6;
-          }
-        }
-      }
-
-      var _iteratorNormalCompletion7 = true;
-      var _didIteratorError7 = false;
-      var _iteratorError7 = undefined;
-
-      try {
-        for (var _iterator7 = this._uniforms[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
-          var _uniform = _step7.value;
-
-          switch (_uniform._length) {
-            case 1:
-              gl.uniform1fv(_uniform._uniform, _uniform._value);break;
-            case 2:
-              gl.uniform2fv(_uniform._uniform, _uniform._value);break;
-            case 3:
-              gl.uniform3fv(_uniform._uniform, _uniform._value);break;
-            case 4:
-              gl.uniform4fv(_uniform._uniform, _uniform._value);break;
-          }
-        }
-      } catch (err) {
-        _didIteratorError7 = true;
-        _iteratorError7 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion7 && _iterator7.return) {
-            _iterator7.return();
-          }
-        } finally {
-          if (_didIteratorError7) {
-            throw _iteratorError7;
-          }
-        }
-      }
-    }
-  }, {
-    key: 'markActive',
-    value: function markActive(frameId) {
-      if (this._activeFrameId != frameId) {
-        this._activeFrameId = frameId;
-        this._completeForActiveFrame = true;
-        for (var i = 0; i < this._samplers.length; ++i) {
-          var sampler = this._samplers[i];
-          if (sampler._renderTexture) {
-            if (!sampler._renderTexture._complete) {
-              this._completeForActiveFrame = false;
-              break;
-            }
-            sampler._renderTexture.markActive(frameId);
-          }
-        }
-      }
-      return this._completeForActiveFrame;
-    }
-
-    // Material State fetchers
-
-  }, {
-    key: '_capsDiff',
-
-
-    // Only really for use from the renderer
-    value: function _capsDiff(otherState) {
-      return otherState & _material.MAT_STATE.CAPS_RANGE ^ this._state & _material.MAT_STATE.CAPS_RANGE;
-    }
-  }, {
-    key: '_blendDiff',
-    value: function _blendDiff(otherState) {
-      if (!(this._state & _material.CAP.BLEND)) {
-        return 0;
-      }
-      return otherState & _material.MAT_STATE.BLEND_FUNC_RANGE ^ this._state & _material.MAT_STATE.BLEND_FUNC_RANGE;
-    }
-  }, {
-    key: '_depthFuncDiff',
-    value: function _depthFuncDiff(otherState) {
-      if (!(this._state & _material.CAP.DEPTH_TEST)) {
-        return 0;
-      }
-      return otherState & _material.MAT_STATE.DEPTH_FUNC_RANGE ^ this._state & _material.MAT_STATE.DEPTH_FUNC_RANGE;
-    }
-  }, {
-    key: 'cullFace',
-    get: function get() {
-      return !!(this._state & _material.CAP.CULL_FACE);
-    }
-  }, {
-    key: 'blend',
-    get: function get() {
-      return !!(this._state & _material.CAP.BLEND);
-    }
-  }, {
-    key: 'depthTest',
-    get: function get() {
-      return !!(this._state & _material.CAP.DEPTH_TEST);
-    }
-  }, {
-    key: 'stencilTest',
-    get: function get() {
-      return !!(this._state & _material.CAP.STENCIL_TEST);
-    }
-  }, {
-    key: 'colorMask',
-    get: function get() {
-      return !!(this._state & _material.CAP.COLOR_MASK);
-    }
-  }, {
-    key: 'depthMask',
-    get: function get() {
-      return !!(this._state & _material.CAP.DEPTH_MASK);
-    }
-  }, {
-    key: 'stencilMask',
-    get: function get() {
-      return !!(this._state & _material.CAP.STENCIL_MASK);
-    }
-  }, {
-    key: 'depthFunc',
-    get: function get() {
-      return ((this._state & _material.MAT_STATE.DEPTH_FUNC_RANGE) >> _material.MAT_STATE.DEPTH_FUNC_SHIFT) + GL.NEVER;
-    }
-  }, {
-    key: 'blendFuncSrc',
-    get: function get() {
-      return (0, _material.stateToBlendFunc)(this._state, _material.MAT_STATE.BLEND_SRC_RANGE, _material.MAT_STATE.BLEND_SRC_SHIFT);
-    }
-  }, {
-    key: 'blendFuncDst',
-    get: function get() {
-      return (0, _material.stateToBlendFunc)(this._state, _material.MAT_STATE.BLEND_DST_RANGE, _material.MAT_STATE.BLEND_DST_SHIFT);
-    }
-  }]);
-
-  return RenderMaterial;
-}();
-
-var Renderer = exports.Renderer = function () {
-  function Renderer(gl) {
-    _classCallCheck(this, Renderer);
-
-    this._gl = gl || createWebGLContext();
-    this._frameId = 0;
-    this._programCache = {};
-    this._textureCache = {};
-    this._renderPrimitives = Array(_material.RENDER_ORDER.DEFAULT);
-    this._cameraPositions = [];
-
-    this._vaoExt = gl.getExtension('OES_vertex_array_object');
-
-    var fragHighPrecision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
-    this._defaultFragPrecision = fragHighPrecision.precision > 0 ? 'highp' : 'mediump';
-
-    this._depthMaskNeedsReset = false;
-    this._colorMaskNeedsReset = false;
-
-    this._globalLightColor = _glMatrix.vec3.clone(DEF_LIGHT_COLOR);
-    this._globalLightDir = _glMatrix.vec3.clone(DEF_LIGHT_DIR);
-  }
-
-  _createClass(Renderer, [{
-    key: 'createRenderBuffer',
-    value: function createRenderBuffer(target, data) {
-      var usage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : GL.STATIC_DRAW;
-
-      var gl = this._gl;
-      var glBuffer = gl.createBuffer();
-
-      if (data instanceof Promise) {
-        var renderBuffer = new RenderBuffer(target, usage, data.then(function (data) {
-          gl.bindBuffer(target, glBuffer);
-          gl.bufferData(target, data, usage);
-          renderBuffer._length = data.byteLength;
-          return glBuffer;
-        }));
-        return renderBuffer;
-      } else {
-        gl.bindBuffer(target, glBuffer);
-        gl.bufferData(target, data, usage);
-        return new RenderBuffer(target, usage, glBuffer, data.byteLength);
-      }
-    }
-  }, {
-    key: 'updateRenderBuffer',
-    value: function updateRenderBuffer(buffer, data) {
-      var _this3 = this;
-
-      var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
-
-      if (buffer._buffer) {
-        var gl = this._gl;
-        gl.bindBuffer(buffer._target, buffer._buffer);
-        if (offset == 0 && buffer._length == data.byteLength) {
-          gl.bufferData(buffer._target, data, buffer._usage);
-        } else {
-          gl.bufferSubData(buffer._target, offset, data);
-        }
-      } else {
-        buffer.waitForComplete().then(function (buffer) {
-          _this3.updateRenderBuffer(buffer, data, offset);
-        });
-      }
-    }
-  }, {
-    key: 'createRenderPrimitive',
-    value: function createRenderPrimitive(primitive, material) {
-      var renderPrimitive = new RenderPrimitive(primitive);
-
-      var program = this._getMaterialProgram(material, renderPrimitive);
-      var renderMaterial = new RenderMaterial(this, material, program);
-      renderPrimitive.setRenderMaterial(renderMaterial);
-
-      if (!this._renderPrimitives[renderMaterial._renderOrder]) {
-        this._renderPrimitives[renderMaterial._renderOrder] = [];
-      }
-
-      this._renderPrimitives[renderMaterial._renderOrder].push(renderPrimitive);
-
-      return renderPrimitive;
-    }
-  }, {
-    key: 'createMesh',
-    value: function createMesh(primitive, material) {
-      var meshNode = new _node.Node();
-      meshNode.addRenderPrimitive(this.createRenderPrimitive(primitive, material));
-      return meshNode;
-    }
-  }, {
-    key: 'drawViews',
-    value: function drawViews(views, rootNode) {
-      if (!rootNode) {
-        return;
-      }
-
-      var gl = this._gl;
-      this._frameId++;
-
-      rootNode.markActive(this._frameId);
-
-      // If there's only one view then flip the algorithm a bit so that we're only
-      // setting the viewport once.
-      if (views.length == 1 && views[0].viewport) {
-        var vp = views[0].viewport;
-        this._gl.viewport(vp.x, vp.y, vp.width, vp.height);
-      }
-
-      // Get the positions of the 'camera' for each view matrix.
-      for (var i = 0; i < views.length; ++i) {
-        _glMatrix.mat4.invert(inverseMatrix, views[i].viewMatrix);
-
-        if (this._cameraPositions.length <= i) {
-          this._cameraPositions.push(_glMatrix.vec3.create());
-        }
-        var cameraPosition = this._cameraPositions[i];
-        _glMatrix.vec3.set(cameraPosition, 0, 0, 0);
-        _glMatrix.vec3.transformMat4(cameraPosition, cameraPosition, inverseMatrix);
-      }
-
-      // Draw each set of render primitives in order
-      var _iteratorNormalCompletion8 = true;
-      var _didIteratorError8 = false;
-      var _iteratorError8 = undefined;
-
-      try {
-        for (var _iterator8 = this._renderPrimitives[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
-          var renderPrimitives = _step8.value;
-
-          if (renderPrimitives && renderPrimitives.length) {
-            this._drawRenderPrimitiveSet(views, renderPrimitives);
-          }
-        }
-      } catch (err) {
-        _didIteratorError8 = true;
-        _iteratorError8 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion8 && _iterator8.return) {
-            _iterator8.return();
-          }
-        } finally {
-          if (_didIteratorError8) {
-            throw _iteratorError8;
-          }
-        }
-      }
-
-      if (this._vaoExt) {
-        this._vaoExt.bindVertexArrayOES(null);
-      }
-
-      if (this._depthMaskNeedsReset) {
-        gl.depthMask(true);
-      }
-      if (this._colorMaskNeedsReset) {
-        gl.colorMask(true, true, true, true);
-      }
-    }
-  }, {
-    key: '_drawRenderPrimitiveSet',
-    value: function _drawRenderPrimitiveSet(views, renderPrimitives) {
-      var gl = this._gl;
-      var program = null;
-      var material = null;
-      var attribMask = 0;
-
-      // Loop through every primitive known to the renderer.
-      var _iteratorNormalCompletion9 = true;
-      var _didIteratorError9 = false;
-      var _iteratorError9 = undefined;
-
-      try {
-        for (var _iterator9 = renderPrimitives[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
-          var primitive = _step9.value;
-
-          // Skip over those that haven't been marked as active for this frame.
-          if (primitive._activeFrameId != this._frameId) {
-            continue;
-          }
-
-          // Bind the primitive material's program if it's different than the one we
-          // were using for the previous primitive.
-          // TODO: The ording of this could be more efficient.
-          if (program != primitive._material._program) {
-            program = primitive._material._program;
-            program.use();
-
-            if (program.uniform.LIGHT_DIRECTION) {
-              gl.uniform3fv(program.uniform.LIGHT_DIRECTION, this._globalLightDir);
-            }
-
-            if (program.uniform.LIGHT_COLOR) {
-              gl.uniform3fv(program.uniform.LIGHT_COLOR, this._globalLightColor);
-            }
-
-            if (views.length == 1) {
-              gl.uniformMatrix4fv(program.uniform.PROJECTION_MATRIX, false, views[0].projectionMatrix);
-              gl.uniformMatrix4fv(program.uniform.VIEW_MATRIX, false, views[0].viewMatrix);
-              gl.uniform3fv(program.uniform.CAMERA_POSITION, this._cameraPositions[0]);
-              gl.uniform1i(program.uniform.EYE_INDEX, views[0].eyeIndex);
-            }
-          }
-
-          if (material != primitive._material) {
-            this._bindMaterialState(primitive._material, material);
-            primitive._material.bind(gl, program, material);
-            material = primitive._material;
-          }
-
-          if (this._vaoExt) {
-            if (primitive._vao) {
-              this._vaoExt.bindVertexArrayOES(primitive._vao);
-            } else {
-              primitive._vao = this._vaoExt.createVertexArrayOES();
-              this._vaoExt.bindVertexArrayOES(primitive._vao);
-              this._bindPrimitive(primitive);
-            }
-          } else {
-            this._bindPrimitive(primitive, attribMask);
-            attribMask = primitive._attributeMask;
-          }
-
-          for (var i = 0; i < views.length; ++i) {
-            var view = views[i];
-            if (views.length > 1) {
-              if (view.viewport) {
-                var vp = view.viewport;
-                gl.viewport(vp.x, vp.y, vp.width, vp.height);
-              }
-              gl.uniformMatrix4fv(program.uniform.PROJECTION_MATRIX, false, view.projectionMatrix);
-              gl.uniformMatrix4fv(program.uniform.VIEW_MATRIX, false, view.viewMatrix);
-              gl.uniform3fv(program.uniform.CAMERA_POSITION, this._cameraPositions[i]);
-              gl.uniform1i(program.uniform.EYE_INDEX, view.eyeIndex);
-            }
-
-            var _iteratorNormalCompletion10 = true;
-            var _didIteratorError10 = false;
-            var _iteratorError10 = undefined;
-
-            try {
-              for (var _iterator10 = primitive._instances[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
-                var instance = _step10.value;
-
-                if (instance._activeFrameId != this._frameId) {
-                  continue;
-                }
-
-                gl.uniformMatrix4fv(program.uniform.MODEL_MATRIX, false, instance.worldMatrix);
-
-                if (primitive._indexBuffer) {
-                  gl.drawElements(primitive._mode, primitive._elementCount, primitive._indexType, primitive._indexByteOffset);
-                } else {
-                  gl.drawArrays(primitive._mode, 0, primitive._elementCount);
-                }
-              }
-            } catch (err) {
-              _didIteratorError10 = true;
-              _iteratorError10 = err;
-            } finally {
-              try {
-                if (!_iteratorNormalCompletion10 && _iterator10.return) {
-                  _iterator10.return();
-                }
-              } finally {
-                if (_didIteratorError10) {
-                  throw _iteratorError10;
-                }
-              }
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError9 = true;
-        _iteratorError9 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion9 && _iterator9.return) {
-            _iterator9.return();
-          }
-        } finally {
-          if (_didIteratorError9) {
-            throw _iteratorError9;
-          }
-        }
-      }
-    }
-  }, {
-    key: '_getRenderTexture',
-    value: function _getRenderTexture(texture) {
-      var _this4 = this;
-
-      if (!texture) {
-        return null;
-      }
-
-      var key = texture.textureKey;
-      if (!key) {
-        throw new Error('Texure does not have a valid key');
-      }
-
-      if (key in this._textureCache) {
-        return this._textureCache[key];
-      } else {
-        var gl = this._gl;
-        var textureHandle = gl.createTexture();
-
-        var renderTexture = new RenderTexture(textureHandle);
-        this._textureCache[key] = renderTexture;
-
-        if (texture instanceof _texture.DataTexture) {
-          gl.bindTexture(gl.TEXTURE_2D, textureHandle);
-          gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.width, texture.height, 0, texture.format, texture._type, texture._data);
-          this._setSamplerParameters(texture);
-          renderTexture._complete = true;
-        } else {
-          texture.waitForComplete().then(function () {
-            gl.bindTexture(gl.TEXTURE_2D, textureHandle);
-            gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.format, gl.UNSIGNED_BYTE, texture.source);
-            _this4._setSamplerParameters(texture);
-            renderTexture._complete = true;
-
-            if (texture instanceof _texture.VideoTexture) {
-              // Once the video starts playing, set a callback to update it's
-              // contents each frame.
-              texture._video.addEventListener('playing', function () {
-                renderTexture._activeCallback = function () {
-                  if (!texture._video.paused && !texture._video.waiting) {
-                    gl.bindTexture(gl.TEXTURE_2D, textureHandle);
-                    gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.format, gl.UNSIGNED_BYTE, texture.source);
-                  }
-                };
-              });
-            }
-          });
-        }
-
-        return renderTexture;
-      }
-    }
-  }, {
-    key: '_setSamplerParameters',
-    value: function _setSamplerParameters(texture) {
-      var gl = this._gl;
-
-      var sampler = texture.sampler;
-      var powerOfTwo = isPowerOfTwo(texture.width) && isPowerOfTwo(texture.height);
-      var mipmap = powerOfTwo && texture.mipmap;
-      if (mipmap) {
-        gl.generateMipmap(gl.TEXTURE_2D);
-      }
-
-      var minFilter = sampler.minFilter || (mipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
-      var wrapS = sampler.wrapS || (powerOfTwo ? gl.REPEAT : gl.CLAMP_TO_EDGE);
-      var wrapT = sampler.wrapT || (powerOfTwo ? gl.REPEAT : gl.CLAMP_TO_EDGE);
-
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, sampler.magFilter || gl.LINEAR);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
-    }
-  }, {
-    key: '_getProgramKey',
-    value: function _getProgramKey(name, defines) {
-      var key = name + ':';
-
-      for (var define in defines) {
-        key += define + '=' + defines[define] + ',';
-      }
-
-      return key;
-    }
-  }, {
-    key: '_getMaterialProgram',
-    value: function _getMaterialProgram(material, renderPrimitive) {
-      var _this5 = this;
-
-      var materialName = material.materialName;
-      var vertexSource = material.vertexSource;
-      var fragmentSource = material.fragmentSource;
-
-      // These should always be defined for every material
-      if (materialName == null) {
-        throw new Error('Material does not have a name');
-      }
-      if (vertexSource == null) {
-        throw new Error('Material "' + materialName + '" does not have a vertex source');
-      }
-      if (fragmentSource == null) {
-        throw new Error('Material "' + materialName + '" does not have a fragment source');
-      }
-
-      var defines = material.getProgramDefines(renderPrimitive);
-      var key = this._getProgramKey(materialName, defines);
-
-      if (key in this._programCache) {
-        return this._programCache[key];
-      } else {
-        var multiview = false; // Handle this dynamically later
-        var fullVertexSource = vertexSource;
-        fullVertexSource += multiview ? VERTEX_SHADER_MULTI_ENTRY : VERTEX_SHADER_SINGLE_ENTRY;
-
-        var precisionMatch = fragmentSource.match(PRECISION_REGEX);
-        var fragPrecisionHeader = precisionMatch ? '' : 'precision ' + this._defaultFragPrecision + ' float;\n';
-
-        var fullFragmentSource = fragPrecisionHeader + fragmentSource;
-        fullFragmentSource += FRAGMENT_SHADER_ENTRY;
-
-        var program = new _program.Program(this._gl, fullVertexSource, fullFragmentSource, ATTRIB, defines);
-        this._programCache[key] = program;
-
-        program.onNextUse(function (program) {
-          // Bind the samplers to the right texture index. This is constant for
-          // the lifetime of the program.
-          for (var i = 0; i < material._samplers.length; ++i) {
-            var sampler = material._samplers[i];
-            var uniform = program.uniform[sampler._uniformName];
-            if (uniform) {
-              _this5._gl.uniform1i(uniform, i);
-            }
-          }
-        });
-
-        return program;
-      }
-    }
-  }, {
-    key: '_bindPrimitive',
-    value: function _bindPrimitive(primitive, attribMask) {
-      var gl = this._gl;
-
-      // If the active attributes have changed then update the active set.
-      if (attribMask != primitive._attributeMask) {
-        for (var attrib in ATTRIB) {
-          if (primitive._attributeMask & ATTRIB_MASK[attrib]) {
-            gl.enableVertexAttribArray(ATTRIB[attrib]);
-          } else {
-            gl.disableVertexAttribArray(ATTRIB[attrib]);
-          }
-        }
-      }
-
-      // Bind the primitive attributes and indices.
-      var _iteratorNormalCompletion11 = true;
-      var _didIteratorError11 = false;
-      var _iteratorError11 = undefined;
-
-      try {
-        for (var _iterator11 = primitive._attributeBuffers[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
-          var attributeBuffer = _step11.value;
-
-          gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer._buffer._buffer);
-          var _iteratorNormalCompletion12 = true;
-          var _didIteratorError12 = false;
-          var _iteratorError12 = undefined;
-
-          try {
-            for (var _iterator12 = attributeBuffer._attributes[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
-              var _attrib = _step12.value;
-
-              gl.vertexAttribPointer(_attrib._attrib_index, _attrib._componentCount, _attrib._componentType, _attrib._normalized, _attrib._stride, _attrib._byteOffset);
-            }
-          } catch (err) {
-            _didIteratorError12 = true;
-            _iteratorError12 = err;
-          } finally {
-            try {
-              if (!_iteratorNormalCompletion12 && _iterator12.return) {
-                _iterator12.return();
-              }
-            } finally {
-              if (_didIteratorError12) {
-                throw _iteratorError12;
-              }
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError11 = true;
-        _iteratorError11 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion11 && _iterator11.return) {
-            _iterator11.return();
-          }
-        } finally {
-          if (_didIteratorError11) {
-            throw _iteratorError11;
-          }
-        }
-      }
-
-      if (primitive._indexBuffer) {
-        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, primitive._indexBuffer._buffer);
-      } else {
-        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-      }
-    }
-  }, {
-    key: '_bindMaterialState',
-    value: function _bindMaterialState(material) {
-      var prevMaterial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
-
-      var gl = this._gl;
-
-      var state = material._state;
-      var prevState = prevMaterial ? prevMaterial._state : ~state;
-
-      // Return early if both materials use identical state
-      if (state == prevState) {
-        return;
-      }
-
-      // Any caps bits changed?
-      if (material._capsDiff(prevState)) {
-        setCap(gl, gl.CULL_FACE, _material.CAP.CULL_FACE, prevState, state);
-        setCap(gl, gl.BLEND, _material.CAP.BLEND, prevState, state);
-        setCap(gl, gl.DEPTH_TEST, _material.CAP.DEPTH_TEST, prevState, state);
-        setCap(gl, gl.STENCIL_TEST, _material.CAP.STENCIL_TEST, prevState, state);
-
-        var colorMaskChange = (state & _material.CAP.COLOR_MASK) - (prevState & _material.CAP.COLOR_MASK);
-        if (colorMaskChange) {
-          var mask = colorMaskChange > 1;
-          this._colorMaskNeedsReset = !mask;
-          gl.colorMask(mask, mask, mask, mask);
-        }
-
-        var depthMaskChange = (state & _material.CAP.DEPTH_MASK) - (prevState & _material.CAP.DEPTH_MASK);
-        if (depthMaskChange) {
-          this._depthMaskNeedsReset = !(depthMaskChange > 1);
-          gl.depthMask(depthMaskChange > 1);
-        }
-
-        var stencilMaskChange = (state & _material.CAP.STENCIL_MASK) - (prevState & _material.CAP.STENCIL_MASK);
-        if (stencilMaskChange) {
-          gl.stencilMask(stencilMaskChange > 1);
-        }
-      }
-
-      // Blending enabled and blend func changed?
-      if (material._blendDiff(prevState)) {
-        gl.blendFunc(material.blendFuncSrc, material.blendFuncDst);
-      }
-
-      // Depth testing enabled and depth func changed?
-      if (material._depthFuncDiff(prevState)) {
-        gl.depthFunc(material.depthFunc);
-      }
-    }
-  }, {
-    key: 'gl',
-    get: function get() {
-      return this._gl;
-    }
-  }, {
-    key: 'globalLightColor',
-    set: function set(value) {
-      _glMatrix.vec3.copy(this._globalLightColor, value);
-    },
-    get: function get() {
-      return _glMatrix.vec3.clone(this._globalLightColor);
-    }
-  }, {
-    key: 'globalLightDir',
-    set: function set(value) {
-      _glMatrix.vec3.copy(this._globalLightDir, value);
-    },
-    get: function get() {
-      return _glMatrix.vec3.clone(this._globalLightDir);
-    }
-  }]);
-
-  return Renderer;
-}();
-
-/***/ }),
-
-/***/ "./src/core/texture.js":
-/*!*****************************!*\
-  !*** ./src/core/texture.js ***!
-  \*****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var GL = WebGLRenderingContext; // For enums
-
-var TextureSampler = exports.TextureSampler = function TextureSampler() {
-  _classCallCheck(this, TextureSampler);
-
-  this.minFilter = null;
-  this.magFilter = null;
-  this.wrapS = null;
-  this.wrapT = null;
-};
-
-var Texture = exports.Texture = function () {
-  function Texture() {
-    _classCallCheck(this, Texture);
-
-    this.sampler = new TextureSampler();
-    this.mipmap = true;
-    // TODO: Anisotropy
-  }
-
-  _createClass(Texture, [{
-    key: 'format',
-    get: function get() {
-      return GL.RGBA;
-    }
-  }, {
-    key: 'width',
-    get: function get() {
-      return 0;
-    }
-  }, {
-    key: 'height',
-    get: function get() {
-      return 0;
-    }
-  }, {
-    key: 'textureKey',
-    get: function get() {
-      return null;
-    }
-  }]);
-
-  return Texture;
-}();
-
-var ImageTexture = exports.ImageTexture = function (_Texture) {
-  _inherits(ImageTexture, _Texture);
-
-  function ImageTexture(img) {
-    _classCallCheck(this, ImageTexture);
-
-    var _this = _possibleConstructorReturn(this, (ImageTexture.__proto__ || Object.getPrototypeOf(ImageTexture)).call(this));
-
-    _this._img = img;
-    _this._imgBitmap = null;
-
-    if (img.src && img.complete) {
-      if (img.naturalWidth) {
-        _this._promise = _this._finishImage();
-      } else {
-        _this._promise = Promise.reject('Image provided had failed to load.');
-      }
-    } else {
-      _this._promise = new Promise(function (resolve, reject) {
-        img.addEventListener('load', function () {
-          return resolve(_this._finishImage());
-        });
-        img.addEventListener('error', reject);
-      });
-    }
-    return _this;
-  }
-
-  _createClass(ImageTexture, [{
-    key: '_finishImage',
-    value: function _finishImage() {
-      var _this2 = this;
-
-      if (window.createImageBitmap) {
-        return window.createImageBitmap(this._img).then(function (imgBitmap) {
-          _this2._imgBitmap = imgBitmap;
-          return Promise.resolve(_this2);
-        });
-      }
-      return Promise.resolve(this);
-    }
-  }, {
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      return this._promise;
-    }
-  }, {
-    key: 'format',
-    get: function get() {
-      // TODO: Can be RGB in some cases.
-      return GL.RGBA;
-    }
-  }, {
-    key: 'width',
-    get: function get() {
-      return this._img.width;
-    }
-  }, {
-    key: 'height',
-    get: function get() {
-      return this._img.height;
-    }
-  }, {
-    key: 'textureKey',
-    get: function get() {
-      return this._img.src;
-    }
-  }, {
-    key: 'source',
-    get: function get() {
-      return this._imgBitmap || this._img;
-    }
-  }]);
-
-  return ImageTexture;
-}(Texture);
-
-var UrlTexture = exports.UrlTexture = function (_ImageTexture) {
-  _inherits(UrlTexture, _ImageTexture);
-
-  function UrlTexture(url) {
-    _classCallCheck(this, UrlTexture);
-
-    var img = new Image();
-
-    var _this3 = _possibleConstructorReturn(this, (UrlTexture.__proto__ || Object.getPrototypeOf(UrlTexture)).call(this, img));
-
-    img.src = url;
-    return _this3;
-  }
-
-  return UrlTexture;
-}(ImageTexture);
-
-var BlobTexture = exports.BlobTexture = function (_ImageTexture2) {
-  _inherits(BlobTexture, _ImageTexture2);
-
-  function BlobTexture(blob) {
-    _classCallCheck(this, BlobTexture);
-
-    var img = new Image();
-
-    var _this4 = _possibleConstructorReturn(this, (BlobTexture.__proto__ || Object.getPrototypeOf(BlobTexture)).call(this, img));
-
-    img.src = window.URL.createObjectURL(blob);
-    return _this4;
-  }
-
-  return BlobTexture;
-}(ImageTexture);
-
-var VideoTexture = exports.VideoTexture = function (_Texture2) {
-  _inherits(VideoTexture, _Texture2);
-
-  function VideoTexture(video) {
-    _classCallCheck(this, VideoTexture);
-
-    var _this5 = _possibleConstructorReturn(this, (VideoTexture.__proto__ || Object.getPrototypeOf(VideoTexture)).call(this));
-
-    _this5._video = video;
-
-    if (video.readyState >= 2) {
-      _this5._promise = Promise.resolve(_this5);
-    } else if (video.error) {
-      _this5._promise = Promise.reject(video.error);
-    } else {
-      _this5._promise = new Promise(function (resolve, reject) {
-        video.addEventListener('loadeddata', function () {
-          return resolve(_this5);
-        });
-        video.addEventListener('error', reject);
-      });
-    }
-    return _this5;
-  }
-
-  _createClass(VideoTexture, [{
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      return this._promise;
-    }
-  }, {
-    key: 'format',
-    get: function get() {
-      // TODO: Can be RGB in some cases.
-      return GL.RGBA;
-    }
-  }, {
-    key: 'width',
-    get: function get() {
-      return this._video.videoWidth;
-    }
-  }, {
-    key: 'height',
-    get: function get() {
-      return this._video.videoHeight;
-    }
-  }, {
-    key: 'textureKey',
-    get: function get() {
-      return this._video.src;
-    }
-  }, {
-    key: 'source',
-    get: function get() {
-      return this._video;
-    }
-  }]);
-
-  return VideoTexture;
-}(Texture);
-
-var nextDataTextureIndex = 0;
-
-var DataTexture = exports.DataTexture = function (_Texture3) {
-  _inherits(DataTexture, _Texture3);
-
-  function DataTexture(data, width, height) {
-    var format = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : GL.RGBA;
-    var type = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : GL.UNSIGNED_BYTE;
-
-    _classCallCheck(this, DataTexture);
-
-    var _this6 = _possibleConstructorReturn(this, (DataTexture.__proto__ || Object.getPrototypeOf(DataTexture)).call(this));
-
-    _this6._data = data;
-    _this6._width = width;
-    _this6._height = height;
-    _this6._format = format;
-    _this6._type = type;
-    _this6._key = 'DATA_' + nextDataTextureIndex;
-    nextDataTextureIndex++;
-    return _this6;
-  }
-
-  _createClass(DataTexture, [{
-    key: 'format',
-    get: function get() {
-      return this._format;
-    }
-  }, {
-    key: 'width',
-    get: function get() {
-      return this._width;
-    }
-  }, {
-    key: 'height',
-    get: function get() {
-      return this._height;
-    }
-  }, {
-    key: 'textureKey',
-    get: function get() {
-      return this._key;
-    }
-  }]);
-
-  return DataTexture;
-}(Texture);
-
-var ColorTexture = exports.ColorTexture = function (_DataTexture) {
-  _inherits(ColorTexture, _DataTexture);
-
-  function ColorTexture(r, g, b, a) {
-    _classCallCheck(this, ColorTexture);
-
-    var colorData = new Uint8Array([r * 255.0, g * 255.0, b * 255.0, a * 255.0]);
-
-    var _this7 = _possibleConstructorReturn(this, (ColorTexture.__proto__ || Object.getPrototypeOf(ColorTexture)).call(this, colorData, 1, 1));
-
-    _this7.mipmap = false;
-    _this7._key = 'COLOR_' + colorData[0] + '_' + colorData[1] + '_' + colorData[2] + '_' + colorData[3];
-    return _this7;
-  }
-
-  return ColorTexture;
-}(DataTexture);
-
-/***/ }),
-
-/***/ "./src/cottontail.js":
-/*!***************************!*\
-  !*** ./src/cottontail.js ***!
-  \***************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _node = __webpack_require__(/*! ./core/node.js */ "./src/core/node.js");
-
-Object.defineProperty(exports, 'Node', {
-  enumerable: true,
-  get: function get() {
-    return _node.Node;
-  }
-});
-
-var _renderer = __webpack_require__(/*! ./core/renderer.js */ "./src/core/renderer.js");
-
-Object.defineProperty(exports, 'Renderer', {
-  enumerable: true,
-  get: function get() {
-    return _renderer.Renderer;
-  }
-});
-Object.defineProperty(exports, 'createWebGLContext', {
-  enumerable: true,
-  get: function get() {
-    return _renderer.createWebGLContext;
-  }
-});
-
-var _texture = __webpack_require__(/*! ./core/texture.js */ "./src/core/texture.js");
-
-Object.defineProperty(exports, 'UrlTexture', {
-  enumerable: true,
-  get: function get() {
-    return _texture.UrlTexture;
-  }
-});
-
-var _primitiveStream = __webpack_require__(/*! ./geometry/primitive-stream.js */ "./src/geometry/primitive-stream.js");
-
-Object.defineProperty(exports, 'PrimitiveStream', {
-  enumerable: true,
-  get: function get() {
-    return _primitiveStream.PrimitiveStream;
-  }
-});
-
-var _boxBuilder = __webpack_require__(/*! ./geometry/box-builder.js */ "./src/geometry/box-builder.js");
-
-Object.defineProperty(exports, 'BoxBuilder', {
-  enumerable: true,
-  get: function get() {
-    return _boxBuilder.BoxBuilder;
-  }
-});
-
-var _pbr = __webpack_require__(/*! ./materials/pbr.js */ "./src/materials/pbr.js");
-
-Object.defineProperty(exports, 'PbrMaterial', {
-  enumerable: true,
-  get: function get() {
-    return _pbr.PbrMaterial;
-  }
-});
-
-var _glMatrix = __webpack_require__(/*! ./math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-Object.defineProperty(exports, 'mat4', {
-  enumerable: true,
-  get: function get() {
-    return _glMatrix.mat4;
-  }
-});
-Object.defineProperty(exports, 'mat3', {
-  enumerable: true,
-  get: function get() {
-    return _glMatrix.mat3;
-  }
-});
-Object.defineProperty(exports, 'vec3', {
-  enumerable: true,
-  get: function get() {
-    return _glMatrix.vec3;
-  }
-});
-Object.defineProperty(exports, 'vec4', {
-  enumerable: true,
-  get: function get() {
-    return _glMatrix.vec4;
-  }
-});
-Object.defineProperty(exports, 'quat', {
-  enumerable: true,
-  get: function get() {
-    return _glMatrix.quat;
-  }
-});
-
-var _boundsRenderer = __webpack_require__(/*! ./nodes/bounds-renderer.js */ "./src/nodes/bounds-renderer.js");
-
-Object.defineProperty(exports, 'BoundsRenderer', {
-  enumerable: true,
-  get: function get() {
-    return _boundsRenderer.BoundsRenderer;
-  }
-});
-
-var _button = __webpack_require__(/*! ./nodes/button.js */ "./src/nodes/button.js");
-
-Object.defineProperty(exports, 'ButtonNode', {
-  enumerable: true,
-  get: function get() {
-    return _button.ButtonNode;
-  }
-});
-
-var _dropShadow = __webpack_require__(/*! ./nodes/drop-shadow.js */ "./src/nodes/drop-shadow.js");
-
-Object.defineProperty(exports, 'DropShadowNode', {
-  enumerable: true,
-  get: function get() {
-    return _dropShadow.DropShadowNode;
-  }
-});
-
-var _cubeSea = __webpack_require__(/*! ./nodes/cube-sea.js */ "./src/nodes/cube-sea.js");
-
-Object.defineProperty(exports, 'CubeSeaNode', {
-  enumerable: true,
-  get: function get() {
-    return _cubeSea.CubeSeaNode;
-  }
-});
-
-var _gltf = __webpack_require__(/*! ./nodes/gltf2.js */ "./src/nodes/gltf2.js");
-
-Object.defineProperty(exports, 'Gltf2Node', {
-  enumerable: true,
-  get: function get() {
-    return _gltf.Gltf2Node;
-  }
-});
-
-var _skybox = __webpack_require__(/*! ./nodes/skybox.js */ "./src/nodes/skybox.js");
-
-Object.defineProperty(exports, 'SkyboxNode', {
-  enumerable: true,
-  get: function get() {
-    return _skybox.SkyboxNode;
-  }
-});
-
-var _video = __webpack_require__(/*! ./nodes/video.js */ "./src/nodes/video.js");
-
-Object.defineProperty(exports, 'VideoNode', {
-  enumerable: true,
-  get: function get() {
-    return _video.VideoNode;
-  }
-});
-
-var _scene = __webpack_require__(/*! ./scenes/scene.js */ "./src/scenes/scene.js");
-
-Object.defineProperty(exports, 'WebXRView', {
-  enumerable: true,
-  get: function get() {
-    return _scene.WebXRView;
-  }
-});
-Object.defineProperty(exports, 'Scene', {
-  enumerable: true,
-  get: function get() {
-    return _scene.Scene;
-  }
-});
-
-var _fallbackHelper = __webpack_require__(/*! ./util/fallback-helper.js */ "./src/util/fallback-helper.js");
-
-Object.defineProperty(exports, 'FallbackHelper', {
-  enumerable: true,
-  get: function get() {
-    return _fallbackHelper.FallbackHelper;
-  }
-});
-
-var _queryArgs = __webpack_require__(/*! ./util/query-args.js */ "./src/util/query-args.js");
-
-Object.defineProperty(exports, 'QueryArgs', {
-  enumerable: true,
-  get: function get() {
-    return _queryArgs.QueryArgs;
-  }
-});
-
-/***/ }),
-
-/***/ "./src/geometry/box-builder.js":
-/*!*************************************!*\
-  !*** ./src/geometry/box-builder.js ***!
-  \*************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.BoxBuilder = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _primitiveStream = __webpack_require__(/*! ./primitive-stream.js */ "./src/geometry/primitive-stream.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var BoxBuilder = exports.BoxBuilder = function (_GeometryBuilderBase) {
-  _inherits(BoxBuilder, _GeometryBuilderBase);
-
-  function BoxBuilder() {
-    _classCallCheck(this, BoxBuilder);
-
-    return _possibleConstructorReturn(this, (BoxBuilder.__proto__ || Object.getPrototypeOf(BoxBuilder)).apply(this, arguments));
-  }
-
-  _createClass(BoxBuilder, [{
-    key: 'pushBox',
-    value: function pushBox(min, max) {
-      var stream = this.primitiveStream;
-
-      var w = max[0] - min[0];
-      var h = max[1] - min[1];
-      var d = max[2] - min[2];
-
-      var wh = w * 0.5;
-      var hh = h * 0.5;
-      var dh = d * 0.5;
-
-      var cx = min[0] + wh;
-      var cy = min[1] + hh;
-      var cz = min[2] + dh;
-
-      stream.startGeometry();
-
-      // Bottom
-      var idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 1, idx + 2);
-      stream.pushTriangle(idx, idx + 2, idx + 3);
-
-      //                 X       Y       Z      U    V    NX    NY   NZ
-      stream.pushVertex(-wh + cx, -hh + cy, -dh + cz, 0.0, 1.0, 0.0, -1.0, 0.0);
-      stream.pushVertex(+wh + cx, -hh + cy, -dh + cz, 1.0, 1.0, 0.0, -1.0, 0.0);
-      stream.pushVertex(+wh + cx, -hh + cy, +dh + cz, 1.0, 0.0, 0.0, -1.0, 0.0);
-      stream.pushVertex(-wh + cx, -hh + cy, +dh + cz, 0.0, 0.0, 0.0, -1.0, 0.0);
-
-      // Top
-      idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 2, idx + 1);
-      stream.pushTriangle(idx, idx + 3, idx + 2);
-
-      stream.pushVertex(-wh + cx, +hh + cy, -dh + cz, 0.0, 0.0, 0.0, 1.0, 0.0);
-      stream.pushVertex(+wh + cx, +hh + cy, -dh + cz, 1.0, 0.0, 0.0, 1.0, 0.0);
-      stream.pushVertex(+wh + cx, +hh + cy, +dh + cz, 1.0, 1.0, 0.0, 1.0, 0.0);
-      stream.pushVertex(-wh + cx, +hh + cy, +dh + cz, 0.0, 1.0, 0.0, 1.0, 0.0);
-
-      // Left
-      idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 2, idx + 1);
-      stream.pushTriangle(idx, idx + 3, idx + 2);
-
-      stream.pushVertex(-wh + cx, -hh + cy, -dh + cz, 0.0, 1.0, -1.0, 0.0, 0.0);
-      stream.pushVertex(-wh + cx, +hh + cy, -dh + cz, 0.0, 0.0, -1.0, 0.0, 0.0);
-      stream.pushVertex(-wh + cx, +hh + cy, +dh + cz, 1.0, 0.0, -1.0, 0.0, 0.0);
-      stream.pushVertex(-wh + cx, -hh + cy, +dh + cz, 1.0, 1.0, -1.0, 0.0, 0.0);
-
-      // Right
-      idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 1, idx + 2);
-      stream.pushTriangle(idx, idx + 2, idx + 3);
-
-      stream.pushVertex(+wh + cx, -hh + cy, -dh + cz, 1.0, 1.0, 1.0, 0.0, 0.0);
-      stream.pushVertex(+wh + cx, +hh + cy, -dh + cz, 1.0, 0.0, 1.0, 0.0, 0.0);
-      stream.pushVertex(+wh + cx, +hh + cy, +dh + cz, 0.0, 0.0, 1.0, 0.0, 0.0);
-      stream.pushVertex(+wh + cx, -hh + cy, +dh + cz, 0.0, 1.0, 1.0, 0.0, 0.0);
-
-      // Back
-      idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 2, idx + 1);
-      stream.pushTriangle(idx, idx + 3, idx + 2);
-
-      stream.pushVertex(-wh + cx, -hh + cy, -dh + cz, 1.0, 1.0, 0.0, 0.0, -1.0);
-      stream.pushVertex(+wh + cx, -hh + cy, -dh + cz, 0.0, 1.0, 0.0, 0.0, -1.0);
-      stream.pushVertex(+wh + cx, +hh + cy, -dh + cz, 0.0, 0.0, 0.0, 0.0, -1.0);
-      stream.pushVertex(-wh + cx, +hh + cy, -dh + cz, 1.0, 0.0, 0.0, 0.0, -1.0);
-
-      // Front
-      idx = stream.nextVertexIndex;
-      stream.pushTriangle(idx, idx + 1, idx + 2);
-      stream.pushTriangle(idx, idx + 2, idx + 3);
-
-      stream.pushVertex(-wh + cx, -hh + cy, +dh + cz, 0.0, 1.0, 0.0, 0.0, 1.0);
-      stream.pushVertex(+wh + cx, -hh + cy, +dh + cz, 1.0, 1.0, 0.0, 0.0, 1.0);
-      stream.pushVertex(+wh + cx, +hh + cy, +dh + cz, 1.0, 0.0, 0.0, 0.0, 1.0);
-      stream.pushVertex(-wh + cx, +hh + cy, +dh + cz, 0.0, 0.0, 0.0, 0.0, 1.0);
-
-      stream.endGeometry();
-    }
-  }, {
-    key: 'pushCube',
-    value: function pushCube() {
-      var center = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [0, 0, 0];
-      var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.0;
-
-      var hs = size * 0.5;
-      this.pushBox([center[0] - hs, center[1] - hs, center[2] - hs], [center[0] + hs, center[1] + hs, center[2] + hs]);
-    }
-  }]);
-
-  return BoxBuilder;
-}(_primitiveStream.GeometryBuilderBase);
-
-/***/ }),
-
-/***/ "./src/geometry/primitive-stream.js":
-/*!******************************************!*\
-  !*** ./src/geometry/primitive-stream.js ***!
-  \******************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.GeometryBuilderBase = exports.PrimitiveStream = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var GL = WebGLRenderingContext; // For enums
-
-var tempVec3 = _glMatrix.vec3.create();
-
-var PrimitiveStream = exports.PrimitiveStream = function () {
-  function PrimitiveStream(options) {
-    _classCallCheck(this, PrimitiveStream);
-
-    this._vertices = [];
-    this._indices = [];
-
-    this._geometryStarted = false;
-
-    this._vertexOffset = 0;
-    this._vertexIndex = 0;
-    this._highIndex = 0;
-
-    this._flipWinding = false;
-    this._invertNormals = false;
-    this._transform = null;
-    this._normalTransform = null;
-    this._min = null;
-    this._max = null;
-  }
-
-  _createClass(PrimitiveStream, [{
-    key: 'startGeometry',
-    value: function startGeometry() {
-      if (this._geometryStarted) {
-        throw new Error('Attempted to start a new geometry before the previous one was ended.');
-      }
-
-      this._geometryStarted = true;
-      this._vertexIndex = 0;
-      this._highIndex = 0;
-    }
-  }, {
-    key: 'endGeometry',
-    value: function endGeometry() {
-      if (!this._geometryStarted) {
-        throw new Error('Attempted to end a geometry before one was started.');
-      }
-
-      if (this._highIndex >= this._vertexIndex) {
-        throw new Error('Geometry contains indices that are out of bounds.\n                       (Contains an index of ' + this._highIndex + ' when the vertex count is ' + this._vertexIndex + ')');
-      }
-
-      this._geometryStarted = false;
-      this._vertexOffset += this._vertexIndex;
-
-      // TODO: Anything else need to be done to finish processing here?
-    }
-  }, {
-    key: 'pushVertex',
-    value: function pushVertex(x, y, z) {
-      var u = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
-      var v = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
-      var nx = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
-      var ny = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0;
-      var nz = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
-
-      if (!this._geometryStarted) {
-        throw new Error('Cannot push vertices before calling startGeometry().');
-      }
-
-      // Transform the incoming vertex if we have a transformation matrix
-      if (this._transform) {
-        tempVec3[0] = x;
-        tempVec3[1] = y;
-        tempVec3[2] = z;
-        _glMatrix.vec3.transformMat4(tempVec3, tempVec3, this._transform);
-        x = tempVec3[0];
-        y = tempVec3[1];
-        z = tempVec3[2];
-
-        tempVec3[0] = nx;
-        tempVec3[1] = ny;
-        tempVec3[2] = nz;
-        _glMatrix.vec3.transformMat3(tempVec3, tempVec3, this._normalTransform);
-        nx = tempVec3[0];
-        ny = tempVec3[1];
-        nz = tempVec3[2];
-      }
-
-      if (this._invertNormals) {
-        nx *= -1.0;
-        ny *= -1.0;
-        nz *= -1.0;
-      }
-
-      this._vertices.push(x, y, z, u, v, nx, ny, nz);
-
-      if (this._min) {
-        this._min[0] = Math.min(this._min[0], x);
-        this._min[1] = Math.min(this._min[1], y);
-        this._min[2] = Math.min(this._min[2], z);
-        this._max[0] = Math.max(this._max[0], x);
-        this._max[1] = Math.max(this._max[1], y);
-        this._max[2] = Math.max(this._max[2], z);
-      } else {
-        this._min = _glMatrix.vec3.fromValues(x, y, z);
-        this._max = _glMatrix.vec3.fromValues(x, y, z);
-      }
-
-      return this._vertexIndex++;
-    }
-  }, {
-    key: 'pushTriangle',
-    value: function pushTriangle(idxA, idxB, idxC) {
-      if (!this._geometryStarted) {
-        throw new Error('Cannot push triangles before calling startGeometry().');
-      }
-
-      this._highIndex = Math.max(this._highIndex, idxA, idxB, idxC);
-
-      idxA += this._vertexOffset;
-      idxB += this._vertexOffset;
-      idxC += this._vertexOffset;
-
-      if (this._flipWinding) {
-        this._indices.push(idxC, idxB, idxA);
-      } else {
-        this._indices.push(idxA, idxB, idxC);
-      }
-    }
-  }, {
-    key: 'clear',
-    value: function clear() {
-      if (this._geometryStarted) {
-        throw new Error('Cannot clear before ending the current geometry.');
-      }
-
-      this._vertices = [];
-      this._indices = [];
-      this._vertexOffset = 0;
-      this._min = null;
-      this._max = null;
-    }
-  }, {
-    key: 'finishPrimitive',
-    value: function finishPrimitive(renderer) {
-      if (!this._vertexOffset) {
-        throw new Error('Attempted to call finishPrimitive() before creating any geometry.');
-      }
-
-      var vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(this._vertices));
-      var indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(this._indices));
-
-      var attribs = [new _primitive.PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 32, 0), new _primitive.PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 32, 12), new _primitive.PrimitiveAttribute('NORMAL', vertexBuffer, 3, GL.FLOAT, 32, 20)];
-
-      var primitive = new _primitive.Primitive(attribs, this._indices.length);
-      primitive.setIndexBuffer(indexBuffer);
-      primitive.setBounds(this._min, this._max);
-
-      return primitive;
-    }
-  }, {
-    key: 'flipWinding',
-    set: function set(value) {
-      if (this._geometryStarted) {
-        throw new Error('Cannot change flipWinding before ending the current geometry.');
-      }
-      this._flipWinding = value;
-    },
-    get: function get() {
-      this._flipWinding;
-    }
-  }, {
-    key: 'invertNormals',
-    set: function set(value) {
-      if (this._geometryStarted) {
-        throw new Error('Cannot change invertNormals before ending the current geometry.');
-      }
-      this._invertNormals = value;
-    },
-    get: function get() {
-      this._invertNormals;
-    }
-  }, {
-    key: 'transform',
-    set: function set(value) {
-      if (this._geometryStarted) {
-        throw new Error('Cannot change transform before ending the current geometry.');
-      }
-      this._transform = value;
-      if (this._transform) {
-        if (!this._normalTransform) {
-          this._normalTransform = _glMatrix.mat3.create();
-        }
-        _glMatrix.mat3.fromMat4(this._normalTransform, this._transform);
-      }
-    },
-    get: function get() {
-      this._transform;
-    }
-  }, {
-    key: 'nextVertexIndex',
-    get: function get() {
-      return this._vertexIndex;
-    }
-  }]);
-
-  return PrimitiveStream;
-}();
-
-var GeometryBuilderBase = exports.GeometryBuilderBase = function () {
-  function GeometryBuilderBase(primitiveStream) {
-    _classCallCheck(this, GeometryBuilderBase);
-
-    if (primitiveStream) {
-      this._stream = primitiveStream;
-    } else {
-      this._stream = new PrimitiveStream();
-    }
-  }
-
-  _createClass(GeometryBuilderBase, [{
-    key: 'finishPrimitive',
-    value: function finishPrimitive(renderer) {
-      return this._stream.finishPrimitive(renderer);
-    }
-  }, {
-    key: 'clear',
-    value: function clear() {
-      this._stream.clear();
-    }
-  }, {
-    key: 'primitiveStream',
-    set: function set(value) {
-      this._stream = value;
-    },
-    get: function get() {
-      return this._stream;
-    }
-  }]);
-
-  return GeometryBuilderBase;
-}();
-
-/***/ }),
-
-/***/ "./src/loaders/gltf2.js":
-/*!******************************!*\
-  !*** ./src/loaders/gltf2.js ***!
-  \******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Gltf2Loader = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var _pbr = __webpack_require__(/*! ../materials/pbr.js */ "./src/materials/pbr.js");
-
-var _node2 = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _texture = __webpack_require__(/*! ../core/texture.js */ "./src/core/texture.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var GL = WebGLRenderingContext; // For enums
-
-var GLB_MAGIC = 0x46546C67;
-var CHUNK_TYPE = {
-  JSON: 0x4E4F534A,
-  BIN: 0x004E4942
-};
-
-function isAbsoluteUri(uri) {
-  var absRegEx = new RegExp('^' + window.location.protocol, 'i');
-  return !!uri.match(absRegEx);
-}
-
-function isDataUri(uri) {
-  var dataRegEx = /^data:/;
-  return !!uri.match(dataRegEx);
-}
-
-function resolveUri(uri, baseUrl) {
-  if (isAbsoluteUri(uri) || isDataUri(uri)) {
-    return uri;
-  }
-  return baseUrl + uri;
-}
-
-function getComponentCount(type) {
-  switch (type) {
-    case 'SCALAR':
-      return 1;
-    case 'VEC2':
-      return 2;
-    case 'VEC3':
-      return 3;
-    case 'VEC4':
-      return 4;
-    default:
-      return 0;
-  }
-}
-
-/**
- * Gltf2SceneLoader
- * Loads glTF 2.0 scenes into a renderable node tree.
- */
-
-var Gltf2Loader = exports.Gltf2Loader = function () {
-  function Gltf2Loader(renderer) {
-    _classCallCheck(this, Gltf2Loader);
-
-    this.renderer = renderer;
-    this._gl = renderer._gl;
-  }
-
-  _createClass(Gltf2Loader, [{
-    key: 'loadFromUrl',
-    value: function loadFromUrl(url) {
-      var _this = this;
-
-      return fetch(url).then(function (response) {
-        var i = url.lastIndexOf('/');
-        var baseUrl = i !== 0 ? url.substring(0, i + 1) : '';
-
-        if (url.endsWith('.gltf')) {
-          return response.json().then(function (json) {
-            return _this.loadFromJson(json, baseUrl);
-          });
-        } else if (url.endsWith('.glb')) {
-          return response.arrayBuffer().then(function (arrayBuffer) {
-            return _this.loadFromBinary(arrayBuffer, baseUrl);
-          });
-        } else {
-          throw new Error('Unrecognized file extension');
-        }
-      });
-    }
-  }, {
-    key: 'loadFromBinary',
-    value: function loadFromBinary(arrayBuffer, baseUrl) {
-      var headerView = new DataView(arrayBuffer, 0, 12);
-      var magic = headerView.getUint32(0, true);
-      var version = headerView.getUint32(4, true);
-      var length = headerView.getUint32(8, true);
-
-      if (magic != GLB_MAGIC) {
-        throw new Error('Invalid magic string in binary header.');
-      }
-
-      if (version != 2) {
-        throw new Error('Incompatible version in binary header.');
-      }
-
-      var chunks = {};
-      var chunkOffset = 12;
-      while (chunkOffset < length) {
-        var chunkHeaderView = new DataView(arrayBuffer, chunkOffset, 8);
-        var chunkLength = chunkHeaderView.getUint32(0, true);
-        var chunkType = chunkHeaderView.getUint32(4, true);
-        chunks[chunkType] = arrayBuffer.slice(chunkOffset + 8, chunkOffset + 8 + chunkLength);
-        chunkOffset += chunkLength + 8;
-      }
-
-      if (!chunks[CHUNK_TYPE.JSON]) {
-        throw new Error('File contained no json chunk.');
-      }
-
-      var decoder = new TextDecoder('utf-8');
-      var jsonString = decoder.decode(chunks[CHUNK_TYPE.JSON]);
-      var json = JSON.parse(jsonString);
-      return this.loadFromJson(json, baseUrl, chunks[CHUNK_TYPE.BIN]);
-    }
-  }, {
-    key: 'loadFromJson',
-    value: function loadFromJson(json, baseUrl, binaryChunk) {
-      if (!json.asset) {
-        throw new Error('Missing asset description.');
-      }
-
-      if (json.asset.minVersion != '2.0' && json.asset.version != '2.0') {
-        throw new Error('Incompatible asset version.');
-      }
-
-      var buffers = [];
-      if (binaryChunk) {
-        buffers[0] = new Gltf2Resource({}, baseUrl, binaryChunk);
-      } else {
-        var _iteratorNormalCompletion = true;
-        var _didIteratorError = false;
-        var _iteratorError = undefined;
-
-        try {
-          for (var _iterator = json.buffers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-            var buffer = _step.value;
-
-            buffers.push(new Gltf2Resource(buffer, baseUrl));
-          }
-        } catch (err) {
-          _didIteratorError = true;
-          _iteratorError = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion && _iterator.return) {
-              _iterator.return();
-            }
-          } finally {
-            if (_didIteratorError) {
-              throw _iteratorError;
-            }
-          }
-        }
-      }
-
-      var bufferViews = [];
-      var _iteratorNormalCompletion2 = true;
-      var _didIteratorError2 = false;
-      var _iteratorError2 = undefined;
-
-      try {
-        for (var _iterator2 = json.bufferViews[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-          var bufferView = _step2.value;
-
-          bufferViews.push(new Gltf2BufferView(bufferView, buffers));
-        }
-      } catch (err) {
-        _didIteratorError2 = true;
-        _iteratorError2 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion2 && _iterator2.return) {
-            _iterator2.return();
-          }
-        } finally {
-          if (_didIteratorError2) {
-            throw _iteratorError2;
-          }
-        }
-      }
-
-      var images = [];
-      if (json.images) {
-        var _iteratorNormalCompletion3 = true;
-        var _didIteratorError3 = false;
-        var _iteratorError3 = undefined;
-
-        try {
-          for (var _iterator3 = json.images[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
-            var image = _step3.value;
-
-            images.push(new Gltf2Resource(image, baseUrl));
-          }
-        } catch (err) {
-          _didIteratorError3 = true;
-          _iteratorError3 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion3 && _iterator3.return) {
-              _iterator3.return();
-            }
-          } finally {
-            if (_didIteratorError3) {
-              throw _iteratorError3;
-            }
-          }
-        }
-      }
-
-      var textures = [];
-      if (json.textures) {
-        var _iteratorNormalCompletion4 = true;
-        var _didIteratorError4 = false;
-        var _iteratorError4 = undefined;
-
-        try {
-          for (var _iterator4 = json.textures[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
-            var texture = _step4.value;
-
-            var _image = images[texture.source];
-            var glTexture = _image.texture(bufferViews);
-            if (texture.sampler) {
-              var sampler = sampler[texture.sampler];
-              glTexture.sampler.minFilter = sampler.minFilter;
-              glTexture.sampler.magFilter = sampler.magFilter;
-              glTexture.sampler.wrapS = sampler.wrapS;
-              glTexture.sampler.wrapT = sampler.wrapT;
-            }
-            textures.push(glTexture);
-          }
-        } catch (err) {
-          _didIteratorError4 = true;
-          _iteratorError4 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion4 && _iterator4.return) {
-              _iterator4.return();
-            }
-          } finally {
-            if (_didIteratorError4) {
-              throw _iteratorError4;
-            }
-          }
-        }
-      }
-
-      function getTexture(textureInfo) {
-        if (!textureInfo) {
-          return null;
-        }
-        return textures[textureInfo.index];
-      }
-
-      var materials = [];
-      if (json.materials) {
-        var _iteratorNormalCompletion5 = true;
-        var _didIteratorError5 = false;
-        var _iteratorError5 = undefined;
-
-        try {
-          for (var _iterator5 = json.materials[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
-            var material = _step5.value;
-
-            var glMaterial = new _pbr.PbrMaterial();
-            var pbr = material.pbrMetallicRoughness || {};
-
-            glMaterial.baseColorFactor.value = pbr.baseColorFactor || [1, 1, 1, 1];
-            glMaterial.baseColor.texture = getTexture(pbr.baseColorTexture);
-            glMaterial.metallicRoughnessFactor.value = [pbr.metallicFactor || 1.0, pbr.roughnessFactor || 1.0];
-            glMaterial.metallicRoughness.texture = getTexture(pbr.metallicRoughnessTexture);
-            glMaterial.normal.texture = getTexture(json.normalTexture);
-            glMaterial.occlusion.texture = getTexture(json.occlusionTexture);
-            glMaterial.occlusionStrength.value = json.occlusionTexture && json.occlusionTexture.strength ? json.occlusionTexture.strength : 1.0;
-            glMaterial.emissiveFactor.value = material.emissiveFactor || [0, 0, 0];
-            glMaterial.emissive.texture = getTexture(json.emissiveTexture);
-            if (!glMaterial.emissive.texture && json.emissiveFactor) {
-              glMaterial.emissive.texture = new _texture.ColorTexture(1.0, 1.0, 1.0, 1.0);
-            }
-
-            switch (material.alphaMode) {
-              case 'BLEND':
-                glMaterial.state.blend = true;
-                break;
-              case 'MASK':
-                // Not really supported.
-                glMaterial.state.blend = true;
-                break;
-              default:
-                // Includes 'OPAQUE'
-                glMaterial.state.blend = false;
-            }
-
-            // glMaterial.alpha_mode = material.alphaMode;
-            // glMaterial.alpha_cutoff = material.alphaCutoff;
-            glMaterial.state.cullFace = !material.doubleSided;
-
-            materials.push(glMaterial);
-          }
-        } catch (err) {
-          _didIteratorError5 = true;
-          _iteratorError5 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion5 && _iterator5.return) {
-              _iterator5.return();
-            }
-          } finally {
-            if (_didIteratorError5) {
-              throw _iteratorError5;
-            }
-          }
-        }
-      }
-
-      var accessors = json.accessors;
-
-      var meshes = [];
-      var _iteratorNormalCompletion6 = true;
-      var _didIteratorError6 = false;
-      var _iteratorError6 = undefined;
-
-      try {
-        for (var _iterator6 = json.meshes[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
-          var mesh = _step6.value;
-
-          var glMesh = new Gltf2Mesh();
-          meshes.push(glMesh);
-
-          var _iteratorNormalCompletion8 = true;
-          var _didIteratorError8 = false;
-          var _iteratorError8 = undefined;
-
-          try {
-            for (var _iterator8 = mesh.primitives[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
-              var primitive = _step8.value;
-
-              var _material = null;
-              if ('material' in primitive) {
-                _material = materials[primitive.material];
-              } else {
-                // Create a "default" material if the primitive has none.
-                _material = new _pbr.PbrMaterial();
-              }
-
-              var attributes = [];
-              var elementCount = 0;
-              /* let glPrimitive = new Gltf2Primitive(primitive, material);
-              glMesh.primitives.push(glPrimitive); */
-
-              var min = null;
-              var max = null;
-
-              for (var name in primitive.attributes) {
-                var accessor = accessors[primitive.attributes[name]];
-                var _bufferView = bufferViews[accessor.bufferView];
-                elementCount = accessor.count;
-
-                var glAttribute = new _primitive.PrimitiveAttribute(name, _bufferView.renderBuffer(this.renderer, GL.ARRAY_BUFFER), getComponentCount(accessor.type), accessor.componentType, _bufferView.byteStride || 0, accessor.byteOffset || 0);
-                glAttribute.normalized = accessor.normalized || false;
-
-                if (name == 'POSITION') {
-                  min = accessor.min;
-                  max = accessor.max;
-                }
-
-                attributes.push(glAttribute);
-              }
-
-              var glPrimitive = new _primitive.Primitive(attributes, elementCount, primitive.mode);
-
-              if ('indices' in primitive) {
-                var _accessor = accessors[primitive.indices];
-                var _bufferView2 = bufferViews[_accessor.bufferView];
-
-                glPrimitive.setIndexBuffer(_bufferView2.renderBuffer(this.renderer, GL.ELEMENT_ARRAY_BUFFER), _accessor.byteOffset || 0, _accessor.componentType);
-                glPrimitive.indexType = _accessor.componentType;
-                glPrimitive.indexByteOffset = _accessor.byteOffset || 0;
-                glPrimitive.elementCount = _accessor.count;
-              }
-
-              if (min && max) {
-                glPrimitive.setBounds(min, max);
-              }
-
-              // After all the attributes have been processed, get a program that is
-              // appropriate for both the material and the primitive attributes.
-              glMesh.primitives.push(this.renderer.createRenderPrimitive(glPrimitive, _material));
-            }
-          } catch (err) {
-            _didIteratorError8 = true;
-            _iteratorError8 = err;
-          } finally {
-            try {
-              if (!_iteratorNormalCompletion8 && _iterator8.return) {
-                _iterator8.return();
-              }
-            } finally {
-              if (_didIteratorError8) {
-                throw _iteratorError8;
-              }
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError6 = true;
-        _iteratorError6 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion6 && _iterator6.return) {
-            _iterator6.return();
-          }
-        } finally {
-          if (_didIteratorError6) {
-            throw _iteratorError6;
-          }
-        }
-      }
-
-      var sceneNode = new _node2.Node();
-      var scene = json.scenes[json.scene];
-      var _iteratorNormalCompletion7 = true;
-      var _didIteratorError7 = false;
-      var _iteratorError7 = undefined;
-
-      try {
-        for (var _iterator7 = scene.nodes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
-          var nodeId = _step7.value;
-
-          var node = json.nodes[nodeId];
-          sceneNode.addNode(this.processNodes(node, json.nodes, meshes));
-        }
-      } catch (err) {
-        _didIteratorError7 = true;
-        _iteratorError7 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion7 && _iterator7.return) {
-            _iterator7.return();
-          }
-        } finally {
-          if (_didIteratorError7) {
-            throw _iteratorError7;
-          }
-        }
-      }
-
-      return sceneNode;
-    }
-  }, {
-    key: 'processNodes',
-    value: function processNodes(node, nodes, meshes) {
-      var glNode = new _node2.Node();
-      glNode.name = node.name;
-
-      if ('mesh' in node) {
-        var mesh = meshes[node.mesh];
-        var _iteratorNormalCompletion9 = true;
-        var _didIteratorError9 = false;
-        var _iteratorError9 = undefined;
-
-        try {
-          for (var _iterator9 = mesh.primitives[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
-            var primitive = _step9.value;
-
-            glNode.addRenderPrimitive(primitive);
-          }
-        } catch (err) {
-          _didIteratorError9 = true;
-          _iteratorError9 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion9 && _iterator9.return) {
-              _iterator9.return();
-            }
-          } finally {
-            if (_didIteratorError9) {
-              throw _iteratorError9;
-            }
-          }
-        }
-      }
-
-      if (node.matrix) {
-        glNode.matrix = new Float32Array(node.matrix);
-      } else if (node.translation || node.rotation || node.scale) {
-        if (node.translation) {
-          glNode.translation = new Float32Array(node.translation);
-        }
-
-        if (node.rotation) {
-          glNode.rotation = new Float32Array(node.rotation);
-        }
-
-        if (node.scale) {
-          glNode.scale = new Float32Array(node.scale);
-        }
-      }
-
-      if (node.children) {
-        var _iteratorNormalCompletion10 = true;
-        var _didIteratorError10 = false;
-        var _iteratorError10 = undefined;
-
-        try {
-          for (var _iterator10 = node.children[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
-            var nodeId = _step10.value;
-
-            var _node = nodes[nodeId];
-            glNode.addNode(this.processNodes(_node, nodes, meshes));
-          }
-        } catch (err) {
-          _didIteratorError10 = true;
-          _iteratorError10 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion10 && _iterator10.return) {
-              _iterator10.return();
-            }
-          } finally {
-            if (_didIteratorError10) {
-              throw _iteratorError10;
-            }
-          }
-        }
-      }
-
-      return glNode;
-    }
-  }]);
-
-  return Gltf2Loader;
-}();
-
-var Gltf2Mesh = function Gltf2Mesh() {
-  _classCallCheck(this, Gltf2Mesh);
-
-  this.primitives = [];
-};
-
-var Gltf2BufferView = function () {
-  function Gltf2BufferView(json, buffers) {
-    _classCallCheck(this, Gltf2BufferView);
-
-    this.buffer = buffers[json.buffer];
-    this.byteOffset = json.byteOffset || 0;
-    this.byteLength = json.byteLength || null;
-    this.byteStride = json.byteStride;
-
-    this._viewPromise = null;
-    this._renderBuffer = null;
-  }
-
-  _createClass(Gltf2BufferView, [{
-    key: 'dataView',
-    value: function dataView() {
-      var _this2 = this;
-
-      if (!this._viewPromise) {
-        this._viewPromise = this.buffer.arrayBuffer().then(function (arrayBuffer) {
-          return new DataView(arrayBuffer, _this2.byteOffset, _this2.byteLength);
-        });
-      }
-      return this._viewPromise;
-    }
-  }, {
-    key: 'renderBuffer',
-    value: function renderBuffer(renderer, target) {
-      if (!this._renderBuffer) {
-        this._renderBuffer = renderer.createRenderBuffer(target, this.dataView());
-      }
-      return this._renderBuffer;
-    }
-  }]);
-
-  return Gltf2BufferView;
-}();
-
-var Gltf2Resource = function () {
-  function Gltf2Resource(json, baseUrl, arrayBuffer) {
-    _classCallCheck(this, Gltf2Resource);
-
-    this.json = json;
-    this.baseUrl = baseUrl;
-
-    this._dataPromise = null;
-    this._texture = null;
-    if (arrayBuffer) {
-      this._dataPromise = Promise.resolve(arrayBuffer);
-    }
-  }
-
-  _createClass(Gltf2Resource, [{
-    key: 'arrayBuffer',
-    value: function arrayBuffer() {
-      if (!this._dataPromise) {
-        if (isDataUri(this.json.uri)) {
-          var base64String = this.json.uri.replace('data:application/octet-stream;base64,', '');
-          var binaryArray = Uint8Array.from(atob(base64String), function (c) {
-            return c.charCodeAt(0);
-          });
-          this._dataPromise = Promise.resolve(binaryArray.buffer);
-          return this._dataPromise;
-        }
-
-        this._dataPromise = fetch(resolveUri(this.json.uri, this.baseUrl)).then(function (response) {
-          return response.arrayBuffer();
-        });
-      }
-      return this._dataPromise;
-    }
-  }, {
-    key: 'texture',
-    value: function texture(bufferViews) {
-      var _this3 = this;
-
-      if (!this._texture) {
-        var img = new Image();
-        this._texture = new _texture.ImageTexture(img);
-
-        if (this.json.uri) {
-          if (isDataUri(this.json.uri)) {
-            img.src = this.json.uri;
-          } else {
-            img.src = '' + this.baseUrl + this.json.uri;
-          }
-        } else {
-          var view = bufferViews[this.json.bufferView];
-          view.dataView().then(function (dataView) {
-            var blob = new Blob([dataView], { type: _this3.json.mimeType });
-            img.src = window.URL.createObjectURL(blob);
-          });
-        }
-      }
-      return this._texture;
-    }
-  }]);
-
-  return Gltf2Resource;
-}();
-
-/***/ }),
-
-/***/ "./src/materials/pbr.js":
-/*!******************************!*\
-  !*** ./src/materials/pbr.js ***!
-  \******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PbrMaterial = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _renderer = __webpack_require__(/*! ../core/renderer.js */ "./src/core/renderer.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var VERTEX_SOURCE = '\nattribute vec3 POSITION, NORMAL;\nattribute vec2 TEXCOORD_0, TEXCOORD_1;\n\nuniform vec3 CAMERA_POSITION;\nuniform vec3 LIGHT_DIRECTION;\n\nvarying vec3 vLight; // Vector from vertex to light.\nvarying vec3 vView; // Vector from vertex to camera.\nvarying vec2 vTex;\n\n#ifdef USE_NORMAL_MAP\nattribute vec4 TANGENT;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_VERTEX_COLOR\nattribute vec4 COLOR_0;\nvarying vec4 vCol;\n#endif\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vec3 n = normalize(vec3(model * vec4(NORMAL, 0.0)));\n#ifdef USE_NORMAL_MAP\n  vec3 t = normalize(vec3(model * vec4(TANGENT.xyz, 0.0)));\n  vec3 b = cross(n, t) * TANGENT.w;\n  vTBN = mat3(t, b, n);\n#else\n  vNorm = n;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  vCol = COLOR_0;\n#endif\n\n  vTex = TEXCOORD_0;\n  vec4 mPos = model * vec4(POSITION, 1.0);\n  vLight = -LIGHT_DIRECTION;\n  vView = CAMERA_POSITION - mPos.xyz;\n  return proj * view * mPos;\n}';
-
-// These equations are borrowed with love from this docs from Epic because I
-// just don't have anything novel to bring to the PBR scene.
-// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
-var EPIC_PBR_FUNCTIONS = '\nvec3 lambertDiffuse(vec3 cDiff) {\n  return cDiff / M_PI;\n}\n\nfloat specD(float a, float nDotH) {\n  float aSqr = a * a;\n  float f = ((nDotH * nDotH) * (aSqr - 1.0) + 1.0);\n  return aSqr / (M_PI * f * f);\n}\n\nfloat specG(float roughness, float nDotL, float nDotV) {\n  float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;\n  float gl = nDotL / (nDotL * (1.0 - k) + k);\n  float gv = nDotV / (nDotV * (1.0 - k) + k);\n  return gl * gv;\n}\n\nvec3 specF(float vDotH, vec3 F0) {\n  float exponent = (-5.55473 * vDotH - 6.98316) * vDotH;\n  float base = 2.0;\n  return F0 + (1.0 - F0) * pow(base, exponent);\n}';
-
-var FRAGMENT_SOURCE = '\n#define M_PI 3.14159265\n\nuniform vec4 baseColorFactor;\n#ifdef USE_BASE_COLOR_MAP\nuniform sampler2D baseColorTex;\n#endif\n\nvarying vec3 vLight;\nvarying vec3 vView;\nvarying vec2 vTex;\n\n#ifdef USE_VERTEX_COLOR\nvarying vec4 vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\nuniform sampler2D normalTex;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_METAL_ROUGH_MAP\nuniform sampler2D metallicRoughnessTex;\n#endif\nuniform vec2 metallicRoughnessFactor;\n\n#ifdef USE_OCCLUSION\nuniform sampler2D occlusionTex;\nuniform float occlusionStrength;\n#endif\n\n#ifdef USE_EMISSIVE_TEXTURE\nuniform sampler2D emissiveTex;\n#endif\nuniform vec3 emissiveFactor;\n\nuniform vec3 LIGHT_COLOR;\n\nconst vec3 dielectricSpec = vec3(0.04);\nconst vec3 black = vec3(0.0);\n\n' + EPIC_PBR_FUNCTIONS + '\n\nvec4 fragment_main() {\n#ifdef USE_BASE_COLOR_MAP\n  vec4 baseColor = texture2D(baseColorTex, vTex) * baseColorFactor;\n#else\n  vec4 baseColor = baseColorFactor;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  baseColor *= vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\n  vec3 n = texture2D(normalTex, vTex).rgb;\n  n = normalize(vTBN * (2.0 * n - 1.0));\n#else\n  vec3 n = normalize(vNorm);\n#endif\n\n#ifdef FULLY_ROUGH\n  float metallic = 0.0;\n#else\n  float metallic = metallicRoughnessFactor.x;\n#endif\n\n  float roughness = metallicRoughnessFactor.y;\n\n#ifdef USE_METAL_ROUGH_MAP\n  vec4 metallicRoughness = texture2D(metallicRoughnessTex, vTex);\n  metallic *= metallicRoughness.b;\n  roughness *= metallicRoughness.g;\n#endif\n  \n  vec3 l = normalize(vLight);\n  vec3 v = normalize(vView);\n  vec3 h = normalize(l+v);\n\n  float nDotL = clamp(dot(n, l), 0.001, 1.0);\n  float nDotV = abs(dot(n, v)) + 0.001;\n  float nDotH = max(dot(n, h), 0.0);\n  float vDotH = max(dot(v, h), 0.0);\n\n  // From GLTF Spec\n  vec3 cDiff = mix(baseColor.rgb * (1.0 - dielectricSpec.r), black, metallic); // Diffuse color\n  vec3 F0 = mix(dielectricSpec, baseColor.rgb, metallic); // Specular color\n  float a = roughness * roughness;\n\n#ifdef FULLY_ROUGH\n  vec3 specular = F0 * 0.45;\n#else\n  vec3 F = specF(vDotH, F0);\n  float D = specD(a, nDotH);\n  float G = specG(roughness, nDotL, nDotV);\n  vec3 specular = (D * F * G) / (4.0 * nDotL * nDotV);\n#endif\n  float halfLambert = dot(n, l) * 0.5 + 0.5;\n  halfLambert *= halfLambert;\n\n  vec3 color = (halfLambert * LIGHT_COLOR * lambertDiffuse(cDiff)) + specular;\n\n#ifdef USE_OCCLUSION\n  float occlusion = texture2D(occlusionTex, vTex).r;\n  color = mix(color, color * occlusion, occlusionStrength);\n#endif\n  \n  vec3 emissive = emissiveFactor;\n#ifdef USE_EMISSIVE_TEXTURE\n  emissive *= texture2D(emissiveTex, vTex).rgb;\n#endif\n  color += emissive;\n\n  // gamma correction\n  //color = pow(color, vec3(1.0/2.2));\n\n  return vec4(color, baseColor.a);\n}';
-
-var PbrMaterial = exports.PbrMaterial = function (_Material) {
-  _inherits(PbrMaterial, _Material);
-
-  function PbrMaterial() {
-    _classCallCheck(this, PbrMaterial);
-
-    var _this = _possibleConstructorReturn(this, (PbrMaterial.__proto__ || Object.getPrototypeOf(PbrMaterial)).call(this));
-
-    _this.baseColor = _this.defineSampler('baseColorTex');
-    _this.metallicRoughness = _this.defineSampler('metallicRoughnessTex');
-    _this.normal = _this.defineSampler('normalTex');
-    _this.occlusion = _this.defineSampler('occlusionTex');
-    _this.emissive = _this.defineSampler('emissiveTex');
-
-    _this.baseColorFactor = _this.defineUniform('baseColorFactor', [1.0, 1.0, 1.0, 1.0]);
-    _this.metallicRoughnessFactor = _this.defineUniform('metallicRoughnessFactor', [1.0, 1.0]);
-    _this.occlusionStrength = _this.defineUniform('occlusionStrength', 1.0);
-    _this.emissiveFactor = _this.defineUniform('emissiveFactor', [0, 0, 0]);
-    return _this;
-  }
-
-  _createClass(PbrMaterial, [{
-    key: 'getProgramDefines',
-    value: function getProgramDefines(renderPrimitive) {
-      var programDefines = {};
-
-      if (renderPrimitive._attributeMask & _renderer.ATTRIB_MASK.COLOR_0) {
-        programDefines['USE_VERTEX_COLOR'] = 1;
-      }
-
-      if (renderPrimitive._attributeMask & _renderer.ATTRIB_MASK.TEXCOORD_0) {
-        if (this.baseColor.texture) {
-          programDefines['USE_BASE_COLOR_MAP'] = 1;
-        }
-
-        if (this.normal.texture && renderPrimitive._attributeMask & _renderer.ATTRIB_MASK.TANGENT) {
-          programDefines['USE_NORMAL_MAP'] = 1;
-        }
-
-        if (this.metallicRoughness.texture) {
-          programDefines['USE_METAL_ROUGH_MAP'] = 1;
-        }
-
-        if (this.occlusion.texture) {
-          programDefines['USE_OCCLUSION'] = 1;
-        }
-
-        if (this.emissive.texture) {
-          programDefines['USE_EMISSIVE_TEXTURE'] = 1;
-        }
-      }
-
-      if ((!this.metallicRoughness.texture || !(renderPrimitive._attributeMask & _renderer.ATTRIB_MASK.TEXCOORD_0)) && this.metallicRoughnessFactor.value[1] == 1.0) {
-        programDefines['FULLY_ROUGH'] = 1;
-      }
-
-      return programDefines;
-    }
-  }, {
-    key: 'materialName',
-    get: function get() {
-      return 'PBR';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return VERTEX_SOURCE;
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return FRAGMENT_SOURCE;
-    }
-  }]);
-
-  return PbrMaterial;
-}(_material.Material);
-
-/***/ }),
-
-/***/ "./src/math/gl-matrix.js":
-/*!*******************************!*\
-  !*** ./src/math/gl-matrix.js ***!
-  \*******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.vec4 = exports.vec3 = exports.vec2 = exports.quat2 = exports.quat = exports.mat4 = exports.mat3 = exports.mat2d = exports.mat2 = exports.glMatrix = undefined;
-
-var _common = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/common.js */ "./node_modules/gl-matrix/src/gl-matrix/common.js");
-
-var glMatrix = _interopRequireWildcard(_common);
-
-var _mat = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/mat2.js */ "./node_modules/gl-matrix/src/gl-matrix/mat2.js");
-
-var mat2 = _interopRequireWildcard(_mat);
-
-var _mat2d = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/mat2d.js */ "./node_modules/gl-matrix/src/gl-matrix/mat2d.js");
-
-var mat2d = _interopRequireWildcard(_mat2d);
-
-var _mat2 = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/mat3.js */ "./node_modules/gl-matrix/src/gl-matrix/mat3.js");
-
-var mat3 = _interopRequireWildcard(_mat2);
-
-var _mat3 = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/mat4.js */ "./node_modules/gl-matrix/src/gl-matrix/mat4.js");
-
-var mat4 = _interopRequireWildcard(_mat3);
-
-var _quat = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/quat.js */ "./node_modules/gl-matrix/src/gl-matrix/quat.js");
-
-var quat = _interopRequireWildcard(_quat);
-
-var _quat2 = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/quat2.js */ "./node_modules/gl-matrix/src/gl-matrix/quat2.js");
-
-var quat2 = _interopRequireWildcard(_quat2);
-
-var _vec = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/vec2.js */ "./node_modules/gl-matrix/src/gl-matrix/vec2.js");
-
-var vec2 = _interopRequireWildcard(_vec);
-
-var _vec2 = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/vec3.js */ "./node_modules/gl-matrix/src/gl-matrix/vec3.js");
-
-var vec3 = _interopRequireWildcard(_vec2);
-
-var _vec3 = __webpack_require__(/*! ../../node_modules/gl-matrix/src/gl-matrix/vec4.js */ "./node_modules/gl-matrix/src/gl-matrix/vec4.js");
-
-var vec4 = _interopRequireWildcard(_vec3);
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
-
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-exports.glMatrix = glMatrix;
-exports.mat2 = mat2;
-exports.mat2d = mat2d;
-exports.mat3 = mat3;
-exports.mat4 = mat4;
-exports.quat = quat;
-exports.quat2 = quat2;
-exports.vec2 = vec2;
-exports.vec3 = vec3;
-exports.vec4 = vec4;
-
-/***/ }),
-
-/***/ "./src/math/ray.js":
-/*!*************************!*\
-  !*** ./src/math/ray.js ***!
-  \*************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Ray = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var _glMatrix = __webpack_require__(/*! ./gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var normalMat = _glMatrix.mat3.create();
-
-var RAY_INTERSECTION_OFFSET = 0.02;
-
-var Ray = exports.Ray = function () {
-  function Ray() {
-    var matrix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
-
-    _classCallCheck(this, Ray);
-
-    this.origin = _glMatrix.vec3.create();
-
-    this._dir = _glMatrix.vec3.create();
-    this._dir[2] = -1.0;
-
-    if (matrix) {
-      _glMatrix.vec3.transformMat4(this.origin, this.origin, matrix);
-      _glMatrix.mat3.fromMat4(normalMat, matrix);
-      _glMatrix.vec3.transformMat3(this._dir, this._dir, normalMat);
-    }
-
-    // To force the inverse and sign calculations.
-    this.dir = this._dir;
-  }
-
-  _createClass(Ray, [{
-    key: 'intersectsAABB',
-
-
-    // Borrowed from:
-    // eslint-disable-next-line max-len
-    // https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
-    value: function intersectsAABB(min, max) {
-      var r = this;
-
-      var bounds = [min, max];
-
-      var tmin = (bounds[r.sign[0]][0] - r.origin[0]) * r.inv_dir[0];
-      var tmax = (bounds[1 - r.sign[0]][0] - r.origin[0]) * r.inv_dir[0];
-      var tymin = (bounds[r.sign[1]][1] - r.origin[1]) * r.inv_dir[1];
-      var tymax = (bounds[1 - r.sign[1]][1] - r.origin[1]) * r.inv_dir[1];
-
-      if (tmin > tymax || tymin > tmax) {
-        return null;
-      }
-      if (tymin > tmin) {
-        tmin = tymin;
-      }
-      if (tymax < tmax) {
-        tmax = tymax;
-      }
-
-      var tzmin = (bounds[r.sign[2]][2] - r.origin[2]) * r.inv_dir[2];
-      var tzmax = (bounds[1 - r.sign[2]][2] - r.origin[2]) * r.inv_dir[2];
-
-      if (tmin > tzmax || tzmin > tmax) {
-        return null;
-      }
-      if (tzmin > tmin) {
-        tmin = tzmin;
-      }
-      if (tzmax < tmax) {
-        tmax = tzmax;
-      }
-
-      var t = -1;
-      if (tmin > 0 && tmax > 0) {
-        t = Math.min(tmin, tmax);
-      } else if (tmin > 0) {
-        t = tmin;
-      } else if (tmax > 0) {
-        t = tmax;
-      } else {
-        // Intersection is behind the ray origin.
-        return null;
-      }
-
-      // Push ray intersection point back along the ray a bit so that cursors
-      // don't accidentally intersect with the hit surface.
-      t -= RAY_INTERSECTION_OFFSET;
-
-      // Return the point where the ray first intersected with the AABB.
-      var intersectionPoint = _glMatrix.vec3.clone(this._dir);
-      _glMatrix.vec3.scale(intersectionPoint, intersectionPoint, t);
-      _glMatrix.vec3.add(intersectionPoint, intersectionPoint, this.origin);
-      return intersectionPoint;
-    }
-  }, {
-    key: 'dir',
-    get: function get() {
-      return this._dir;
-    },
-    set: function set(value) {
-      this._dir = _glMatrix.vec3.copy(this._dir, value);
-      _glMatrix.vec3.normalize(this._dir, this._dir);
-
-      this.inv_dir = _glMatrix.vec3.fromValues(1.0 / this._dir[0], 1.0 / this._dir[1], 1.0 / this._dir[2]);
-
-      this.sign = [this.inv_dir[0] < 0 ? 1 : 0, this.inv_dir[1] < 0 ? 1 : 0, this.inv_dir[2] < 0 ? 1 : 0];
-    }
-  }]);
-
-  return Ray;
-}();
-
-/***/ }),
-
-/***/ "./src/nodes/bounds-renderer.js":
-/*!**************************************!*\
-  !*** ./src/nodes/bounds-renderer.js ***!
-  \**************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.BoundsRenderer = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-This file renders a passed in XRStageBounds object and attempts
-to render geometry on the floor to indicate where the bounds is.
-XRStageBounds' `geometry` is a series of XRStageBoundsPoints (in
-clockwise-order) with `x` and `z` properties for each.
-*/
-
-var GL = WebGLRenderingContext; // For enums
-
-var BoundsMaterial = function (_Material) {
-  _inherits(BoundsMaterial, _Material);
-
-  function BoundsMaterial() {
-    _classCallCheck(this, BoundsMaterial);
-
-    var _this = _possibleConstructorReturn(this, (BoundsMaterial.__proto__ || Object.getPrototypeOf(BoundsMaterial)).call(this));
-
-    _this.state.blend = true;
-    _this.state.blendFuncSrc = GL.SRC_ALPHA;
-    _this.state.blendFuncDst = GL.ONE;
-    _this.state.depthTest = false;
-    return _this;
-  }
-
-  _createClass(BoundsMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'BOUNDS_RENDERER';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    precision mediump float;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 1.0, 0.0, 0.3);\n    }';
-    }
-  }]);
-
-  return BoundsMaterial;
-}(_material.Material);
-
-var BoundsRenderer = exports.BoundsRenderer = function (_Node) {
-  _inherits(BoundsRenderer, _Node);
-
-  function BoundsRenderer() {
-    _classCallCheck(this, BoundsRenderer);
-
-    var _this2 = _possibleConstructorReturn(this, (BoundsRenderer.__proto__ || Object.getPrototypeOf(BoundsRenderer)).call(this));
-
-    _this2._stageBounds = null;
-    return _this2;
-  }
-
-  _createClass(BoundsRenderer, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      this.stageBounds = this._stageBounds;
-    }
-  }, {
-    key: 'stageBounds',
-    get: function get() {
-      return this._stageBounds;
-    },
-    set: function set(stageBounds) {
-      if (this._stageBounds) {
-        this.clearRenderPrimitives();
-      }
-      this._stageBounds = stageBounds;
-      if (!stageBounds || stageBounds.length === 0 || !this._renderer) {
-        return;
-      }
-
-      var verts = [];
-      var indices = [];
-
-      // Tessellate the bounding points from XRStageBounds and connect
-      // each point to a neighbor and 0,0,0.
-      var pointCount = stageBounds.geometry.length;
-      for (var i = 0; i < pointCount; i++) {
-        var point = stageBounds.geometry[i];
-        verts.push(point.x, 0, point.z);
-        indices.push(i, i === 0 ? pointCount - 1 : i - 1, pointCount);
-      }
-      // Center point
-      verts.push(0, 0, 0);
-
-      var vertexBuffer = this._renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(verts));
-      var indexBuffer = this._renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));
-
-      var attribs = [new _primitive.PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 12, 0)];
-
-      var primitive = new _primitive.Primitive(attribs, indices.length);
-      primitive.setIndexBuffer(indexBuffer);
-
-      var renderPrimitive = this._renderer.createRenderPrimitive(primitive, new BoundsMaterial());
-      this.addRenderPrimitive(renderPrimitive);
-    }
-  }]);
-
-  return BoundsRenderer;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/button.js":
-/*!*****************************!*\
-  !*** ./src/nodes/button.js ***!
-  \*****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.ButtonNode = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitiveStream = __webpack_require__(/*! ../geometry/primitive-stream.js */ "./src/geometry/primitive-stream.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var BUTTON_SIZE = 0.1;
-var BUTTON_CORNER_RADIUS = 0.025;
-var BUTTON_CORNER_SEGMENTS = 8;
-var BUTTON_ICON_SIZE = 0.07;
-var BUTTON_LAYER_DISTANCE = 0.005;
-var BUTTON_COLOR = 0.75;
-var BUTTON_ALPHA = 0.85;
-var BUTTON_HOVER_COLOR = 0.9;
-var BUTTON_HOVER_ALPHA = 1.0;
-var BUTTON_HOVER_SCALE = 1.1;
-var BUTTON_HOVER_TRANSITION_TIME_MS = 200;
-
-var ButtonMaterial = function (_Material) {
-  _inherits(ButtonMaterial, _Material);
-
-  function ButtonMaterial() {
-    _classCallCheck(this, ButtonMaterial);
-
-    var _this = _possibleConstructorReturn(this, (ButtonMaterial.__proto__ || Object.getPrototypeOf(ButtonMaterial)).call(this));
-
-    _this.state.blend = true;
-
-    _this.defineUniform('hoverAmount', 0);
-    return _this;
-  }
-
-  _createClass(ButtonMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'BUTTON_MATERIAL';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n\n    uniform float hoverAmount;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      float scale = mix(1.0, ' + BUTTON_HOVER_SCALE + ', hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    uniform float hoverAmount;\n\n    const vec4 default_color = vec4(' + BUTTON_COLOR + ', ' + BUTTON_COLOR + ', ' + BUTTON_COLOR + ', ' + BUTTON_ALPHA + ');\n    const vec4 hover_color = vec4(' + BUTTON_HOVER_COLOR + ', ' + BUTTON_HOVER_COLOR + ',\n                                  ' + BUTTON_HOVER_COLOR + ', ' + BUTTON_HOVER_ALPHA + ');\n\n    vec4 fragment_main() {\n      return mix(default_color, hover_color, hoverAmount);\n    }';
-    }
-  }]);
-
-  return ButtonMaterial;
-}(_material.Material);
-
-var ButtonIconMaterial = function (_Material2) {
-  _inherits(ButtonIconMaterial, _Material2);
-
-  function ButtonIconMaterial() {
-    _classCallCheck(this, ButtonIconMaterial);
-
-    var _this2 = _possibleConstructorReturn(this, (ButtonIconMaterial.__proto__ || Object.getPrototypeOf(ButtonIconMaterial)).call(this));
-
-    _this2.state.blend = true;
-
-    _this2.defineUniform('hoverAmount', 0);
-    _this2.icon = _this2.defineSampler('icon');
-    return _this2;
-  }
-
-  _createClass(ButtonIconMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'BUTTON_ICON_MATERIAL';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    uniform float hoverAmount;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      float scale = mix(1.0, ' + BUTTON_HOVER_SCALE + ', hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    uniform sampler2D icon;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(icon, vTexCoord);\n    }';
-    }
-  }]);
-
-  return ButtonIconMaterial;
-}(_material.Material);
-
-var ButtonNode = exports.ButtonNode = function (_Node) {
-  _inherits(ButtonNode, _Node);
-
-  function ButtonNode(iconTexture, callback) {
-    _classCallCheck(this, ButtonNode);
-
-    // All buttons are selectable by default.
-    var _this3 = _possibleConstructorReturn(this, (ButtonNode.__proto__ || Object.getPrototypeOf(ButtonNode)).call(this));
-
-    _this3.selectable = true;
-
-    _this3._selectHandler = callback;
-    _this3._iconTexture = iconTexture;
-    _this3._hovered = false;
-    _this3._hoverT = 0;
-    return _this3;
-  }
-
-  _createClass(ButtonNode, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      var stream = new _primitiveStream.PrimitiveStream();
-
-      var hd = BUTTON_LAYER_DISTANCE * 0.5;
-
-      // Build a rounded rect for the background.
-      var hs = BUTTON_SIZE * 0.5;
-      var ihs = hs - BUTTON_CORNER_RADIUS;
-      stream.startGeometry();
-
-      // Rounded corners and sides
-      var segments = BUTTON_CORNER_SEGMENTS * 4;
-      for (var i = 0; i < segments; ++i) {
-        var rad = i * (Math.PI * 2.0 / segments);
-        var x = Math.cos(rad) * BUTTON_CORNER_RADIUS;
-        var y = Math.sin(rad) * BUTTON_CORNER_RADIUS;
-        var section = Math.floor(i / BUTTON_CORNER_SEGMENTS);
-        switch (section) {
-          case 0:
-            x += ihs;
-            y += ihs;
-            break;
-          case 1:
-            x -= ihs;
-            y += ihs;
-            break;
-          case 2:
-            x -= ihs;
-            y -= ihs;
-            break;
-          case 3:
-            x += ihs;
-            y -= ihs;
-            break;
-        }
-
-        stream.pushVertex(x, y, -hd, 0, 0, 0, 0, 1);
-
-        if (i > 1) {
-          stream.pushTriangle(0, i - 1, i);
-        }
-      }
-
-      stream.endGeometry();
-
-      var buttonPrimitive = stream.finishPrimitive(renderer);
-      this._buttonRenderPrimitive = renderer.createRenderPrimitive(buttonPrimitive, new ButtonMaterial());
-      this.addRenderPrimitive(this._buttonRenderPrimitive);
-
-      // Build a simple textured quad for the foreground.
-      hs = BUTTON_ICON_SIZE * 0.5;
-      stream.clear();
-      stream.startGeometry();
-
-      stream.pushVertex(-hs, hs, hd, 0, 0, 0, 0, 1);
-      stream.pushVertex(-hs, -hs, hd, 0, 1, 0, 0, 1);
-      stream.pushVertex(hs, -hs, hd, 1, 1, 0, 0, 1);
-      stream.pushVertex(hs, hs, hd, 1, 0, 0, 0, 1);
-
-      stream.pushTriangle(0, 1, 2);
-      stream.pushTriangle(0, 2, 3);
-
-      stream.endGeometry();
-
-      var iconPrimitive = stream.finishPrimitive(renderer);
-      var iconMaterial = new ButtonIconMaterial();
-      iconMaterial.icon.texture = this._iconTexture;
-      this._iconRenderPrimitive = renderer.createRenderPrimitive(iconPrimitive, iconMaterial);
-      this.addRenderPrimitive(this._iconRenderPrimitive);
-    }
-  }, {
-    key: 'onHoverStart',
-    value: function onHoverStart() {
-      this._hovered = true;
-    }
-  }, {
-    key: 'onHoverEnd',
-    value: function onHoverEnd() {
-      this._hovered = false;
-    }
-  }, {
-    key: '_updateHoverState',
-    value: function _updateHoverState() {
-      var t = this._hoverT / BUTTON_HOVER_TRANSITION_TIME_MS;
-      // Cubic Ease In/Out
-      // TODO: Get a better animation system
-      var hoverAmount = t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
-      this._buttonRenderPrimitive.uniforms.hoverAmount.value = hoverAmount;
-      this._iconRenderPrimitive.uniforms.hoverAmount.value = hoverAmount;
-    }
-  }, {
-    key: 'onUpdate',
-    value: function onUpdate(timestamp, frameDelta) {
-      if (this._hovered && this._hoverT < BUTTON_HOVER_TRANSITION_TIME_MS) {
-        this._hoverT = Math.min(BUTTON_HOVER_TRANSITION_TIME_MS, this._hoverT + frameDelta);
-        this._updateHoverState();
-      } else if (!this._hovered && this._hoverT > 0) {
-        this._hoverT = Math.max(0.0, this._hoverT - frameDelta);
-        this._updateHoverState();
-      }
-    }
-  }, {
-    key: 'iconTexture',
-    get: function get() {
-      return this._iconTexture;
-    },
-    set: function set(value) {
-      if (this._iconTexture == value) {
-        return;
-      }
-
-      this._iconTexture = value;
-      this._iconRenderPrimitive.samplers.icon.texture = value;
-    }
-  }]);
-
-  return ButtonNode;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/cube-sea.js":
-/*!*******************************!*\
-  !*** ./src/nodes/cube-sea.js ***!
-  \*******************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.CubeSeaNode = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _texture = __webpack_require__(/*! ../core/texture.js */ "./src/core/texture.js");
-
-var _boxBuilder = __webpack_require__(/*! ../geometry/box-builder.js */ "./src/geometry/box-builder.js");
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var CubeSeaMaterial = function (_Material) {
-  _inherits(CubeSeaMaterial, _Material);
-
-  function CubeSeaMaterial() {
-    var heavy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
-
-    _classCallCheck(this, CubeSeaMaterial);
-
-    var _this = _possibleConstructorReturn(this, (CubeSeaMaterial.__proto__ || Object.getPrototypeOf(CubeSeaMaterial)).call(this));
-
-    _this.heavy = heavy;
-
-    _this.baseColor = _this.defineSampler('baseColor');
-    return _this;
-  }
-
-  _createClass(CubeSeaMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'CUBE_SEA';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    attribute vec3 NORMAL;\n\n    varying vec2 vTexCoord;\n    varying vec3 vLight;\n\n    const vec3 lightDir = vec3(0.75, 0.5, 1.0);\n    const vec3 ambientColor = vec3(0.5, 0.5, 0.5);\n    const vec3 lightColor = vec3(0.75, 0.75, 0.75);\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec3 normalRotated = vec3(model * vec4(NORMAL, 0.0));\n      float lightFactor = max(dot(normalize(lightDir), normalRotated), 0.0);\n      vLight = ambientColor + (lightColor * lightFactor);\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      if (!this.heavy) {
-        return '\n      precision mediump float;\n      uniform sampler2D baseColor;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec4 fragment_main() {\n        return vec4(vLight, 1.0) * texture2D(baseColor, vTexCoord);\n      }';
-      } else {
-        // Used when we want to stress the GPU a bit more.
-        // Stolen with love from https://www.clicktorelease.com/code/codevember-2016/4/
-        return '\n      precision mediump float;\n\n      uniform sampler2D diffuse;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec2 dimensions = vec2(64, 64);\n      float seed = 0.42;\n\n      vec2 hash( vec2 p ) {\n        p=vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3)));\n        return fract(sin(p)*18.5453);\n      }\n\n      vec3 hash3( vec2 p ) {\n          vec3 q = vec3( dot(p,vec2(127.1,311.7)),\n                 dot(p,vec2(269.5,183.3)),\n                 dot(p,vec2(419.2,371.9)) );\n        return fract(sin(q)*43758.5453);\n      }\n\n      float iqnoise( in vec2 x, float u, float v ) {\n        vec2 p = floor(x);\n        vec2 f = fract(x);\n        float k = 1.0+63.0*pow(1.0-v,4.0);\n        float va = 0.0;\n        float wt = 0.0;\n        for( int j=-2; j<=2; j++ )\n          for( int i=-2; i<=2; i++ ) {\n            vec2 g = vec2( float(i),float(j) );\n            vec3 o = hash3( p + g )*vec3(u,u,1.0);\n            vec2 r = g - f + o.xy;\n            float d = dot(r,r);\n            float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n            va += o.z*ww;\n            wt += ww;\n          }\n        return va/wt;\n      }\n\n      // return distance, and cell id\n      vec2 voronoi( in vec2 x ) {\n        vec2 n = floor( x );\n        vec2 f = fract( x );\n        vec3 m = vec3( 8.0 );\n        for( int j=-1; j<=1; j++ )\n          for( int i=-1; i<=1; i++ ) {\n            vec2  g = vec2( float(i), float(j) );\n            vec2  o = hash( n + g );\n            vec2  r = g - f + (0.5+0.5*sin(seed+6.2831*o));\n            float d = dot( r, r );\n            if( d<m.x )\n              m = vec3( d, o );\n          }\n        return vec2( sqrt(m.x), m.y+m.z );\n      }\n\n      vec4 fragment_main() {\n        vec2 uv = ( vTexCoord );\n        uv *= vec2( 10., 10. );\n        uv += seed;\n        vec2 p = 0.5 - 0.5*sin( 0.*vec2(1.01,1.71) );\n\n        vec2 c = voronoi( uv );\n        vec3 col = vec3( c.y / 2. );\n\n        float f = iqnoise( 1. * uv + c.y, p.x, p.y );\n        col *= 1.0 + .25 * vec3( f );\n\n        return vec4(vLight, 1.0) * texture2D(diffuse, vTexCoord) * vec4( col, 1. );\n      }';
-      }
-    }
-  }]);
-
-  return CubeSeaMaterial;
-}(_material.Material);
-
-var CubeSeaNode = exports.CubeSeaNode = function (_Node) {
-  _inherits(CubeSeaNode, _Node);
-
-  function CubeSeaNode() {
-    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
-
-    _classCallCheck(this, CubeSeaNode);
-
-    // Test variables
-    // If true, use a very heavyweight shader to stress the GPU.
-    var _this2 = _possibleConstructorReturn(this, (CubeSeaNode.__proto__ || Object.getPrototypeOf(CubeSeaNode)).call(this));
-
-    _this2.heavyGpu = !!options.heavyGpu;
-
-    // Number and size of the static cubes. Warning, large values
-    // don't render right due to overflow of the int16 indices.
-    _this2.cubeCount = options.cubeCount || (_this2.heavyGpu ? 12 : 10);
-    _this2.cubeScale = options.cubeScale || 1.0;
-
-    // Draw only half the world cubes. Helps test variable render cost
-    // when combined with heavyGpu.
-    _this2.halfOnly = !!options.halfOnly;
-
-    // Automatically spin the world cubes. Intended for automated testing,
-    // not recommended for viewing in a headset.
-    _this2.autoRotate = !!options.autoRotate;
-
-    _this2._texture = new _texture.UrlTexture(options.imageUrl || 'media/textures/cube-sea.png');
-
-    _this2._material = new CubeSeaMaterial(_this2.heavyGpu);
-    _this2._material.baseColor.texture = _this2._texture;
-
-    _this2._renderPrimitive = null;
-    return _this2;
-  }
-
-  _createClass(CubeSeaNode, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      this._renderPrimitive = null;
-
-      var boxBuilder = new _boxBuilder.BoxBuilder();
-
-      // Build the spinning "hero" cubes
-      boxBuilder.pushCube([0, 0.25, -0.8], 0.1);
-      boxBuilder.pushCube([0.8, 0.25, 0], 0.1);
-      boxBuilder.pushCube([0, 0.25, 0.8], 0.1);
-      boxBuilder.pushCube([-0.8, 0.25, 0], 0.1);
-
-      var heroPrimitive = boxBuilder.finishPrimitive(renderer);
-
-      this.heroNode = renderer.createMesh(heroPrimitive, this._material);
-
-      this.rebuildCubes(boxBuilder);
-
-      this.cubeSeaNode = new _node.Node();
-      this.cubeSeaNode.addRenderPrimitive(this._renderPrimitive);
-
-      this.addNode(this.cubeSeaNode);
-      this.addNode(this.heroNode);
-
-      return this.waitForComplete();
-    }
-  }, {
-    key: 'rebuildCubes',
-    value: function rebuildCubes(boxBuilder) {
-      if (!this._renderer) {
-        return;
-      }
-
-      if (!boxBuilder) {
-        boxBuilder = new _boxBuilder.BoxBuilder();
-      } else {
-        boxBuilder.clear();
-      }
-
-      var size = 0.4 * this.cubeScale;
-
-      // Build the cube sea
-      var halfGrid = this.cubeCount * 0.5;
-      for (var x = 0; x < this.cubeCount; ++x) {
-        for (var y = 0; y < this.cubeCount; ++y) {
-          for (var z = 0; z < this.cubeCount; ++z) {
-            var pos = [x - halfGrid, y - halfGrid, z - halfGrid];
-            // Only draw cubes on one side. Useful for testing variable render
-            // cost that depends on view direction.
-            if (this.halfOnly && pos[0] < 0) {
-              continue;
-            }
-
-            // Don't place a cube in the center of the grid.
-            if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0) {
-              continue;
-            }
-
-            boxBuilder.pushCube(pos, size);
-          }
-        }
-      }
-
-      if (this.cubeCount > 12) {
-        // Each cube has 6 sides with 2 triangles and 3 indices per triangle, so
-        // the total number of indices needed is cubeCount^3 * 36. This exceeds
-        // the short index range past 12 cubes.
-        boxBuilder.indexType = 5125; // gl.UNSIGNED_INT
-      }
-      var cubeSeaPrimitive = boxBuilder.finishPrimitive(this._renderer);
-
-      if (!this._renderPrimitive) {
-        this._renderPrimitive = this._renderer.createRenderPrimitive(cubeSeaPrimitive, this._material);
-      } else {
-        this._renderPrimitive.setPrimitive(cubeSeaPrimitive);
-      }
-    }
-  }, {
-    key: 'onUpdate',
-    value: function onUpdate(timestamp, frameDelta) {
-      if (this.autoRotate) {
-        _glMatrix.mat4.fromRotation(this.cubeSeaNode.matrix, timestamp / 500, [0, -1, 0]);
-      }
-      _glMatrix.mat4.fromRotation(this.heroNode.matrix, timestamp / 2000, [0, 1, 0]);
-    }
-  }]);
-
-  return CubeSeaNode;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/drop-shadow.js":
-/*!**********************************!*\
-  !*** ./src/nodes/drop-shadow.js ***!
-  \**********************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.DropShadowNode = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitiveStream = __webpack_require__(/*! ../geometry/primitive-stream.js */ "./src/geometry/primitive-stream.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var GL = WebGLRenderingContext; // For enums
-
-var SHADOW_SEGMENTS = 32;
-var SHADOW_GROUND_OFFSET = 0.01;
-var SHADOW_CENTER_ALPHA = 0.7;
-var SHADOW_INNER_ALPHA = 0.3;
-var SHADOW_OUTER_ALPHA = 0.0;
-var SHADOW_INNER_RADIUS = 0.6;
-var SHADOW_OUTER_RADIUS = 1.0;
-
-var DropShadowMaterial = function (_Material) {
-  _inherits(DropShadowMaterial, _Material);
-
-  function DropShadowMaterial() {
-    _classCallCheck(this, DropShadowMaterial);
-
-    var _this = _possibleConstructorReturn(this, (DropShadowMaterial.__proto__ || Object.getPrototypeOf(DropShadowMaterial)).call(this));
-
-    _this.state.blend = true;
-    _this.state.blendFuncSrc = GL.ONE;
-    _this.state.blendFuncDst = GL.ONE_MINUS_SRC_ALPHA;
-    _this.state.depthFunc = GL.LEQUAL;
-    _this.state.depthMask = false;
-    return _this;
-  }
-
-  _createClass(DropShadowMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'DROP_SHADOW_MATERIAL';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying float vShadow;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vShadow = TEXCOORD_0.x;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    varying float vShadow;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 0.0, 0.0, vShadow);\n    }';
-    }
-  }]);
-
-  return DropShadowMaterial;
-}(_material.Material);
-
-var DropShadowNode = exports.DropShadowNode = function (_Node) {
-  _inherits(DropShadowNode, _Node);
-
-  function DropShadowNode(iconTexture, callback) {
-    _classCallCheck(this, DropShadowNode);
-
-    return _possibleConstructorReturn(this, (DropShadowNode.__proto__ || Object.getPrototypeOf(DropShadowNode)).call(this));
-  }
-
-  _createClass(DropShadowNode, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      var stream = new _primitiveStream.PrimitiveStream();
-
-      stream.startGeometry();
-
-      // Shadow center
-      stream.pushVertex(0, SHADOW_GROUND_OFFSET, 0, SHADOW_CENTER_ALPHA);
-
-      var segRad = Math.PI * 2.0 / SHADOW_SEGMENTS;
-
-      var idx = void 0;
-      for (var i = 0; i < SHADOW_SEGMENTS; ++i) {
-        idx = stream.nextVertexIndex;
-
-        var rad = i * segRad;
-        var x = Math.cos(rad);
-        var y = Math.sin(rad);
-        stream.pushVertex(x * SHADOW_INNER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_INNER_RADIUS, SHADOW_INNER_ALPHA);
-        stream.pushVertex(x * SHADOW_OUTER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_OUTER_RADIUS, SHADOW_OUTER_ALPHA);
-
-        if (i > 0) {
-          // Inner circle
-          stream.pushTriangle(0, idx, idx - 2);
-
-          // Outer circle
-          stream.pushTriangle(idx, idx + 1, idx - 1);
-          stream.pushTriangle(idx, idx - 1, idx - 2);
-        }
-      }
-
-      stream.pushTriangle(0, 1, idx);
-
-      stream.pushTriangle(1, 2, idx + 1);
-      stream.pushTriangle(1, idx + 1, idx);
-
-      stream.endGeometry();
-
-      var shadowPrimitive = stream.finishPrimitive(renderer);
-      this._shadowRenderPrimitive = renderer.createRenderPrimitive(shadowPrimitive, new DropShadowMaterial());
-      this.addRenderPrimitive(this._shadowRenderPrimitive);
-    }
-  }]);
-
-  return DropShadowNode;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/gltf2.js":
-/*!****************************!*\
-  !*** ./src/nodes/gltf2.js ***!
-  \****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Gltf2Node = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _gltf = __webpack_require__(/*! ../loaders/gltf2.js */ "./src/loaders/gltf2.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-// Using a weak map here allows us to cache a loader per-renderer without
-// modifying the renderer object or leaking memory when it's garbage collected.
-var gltfLoaderMap = new WeakMap();
-
-var Gltf2Node = exports.Gltf2Node = function (_Node) {
-  _inherits(Gltf2Node, _Node);
-
-  function Gltf2Node(options) {
-    _classCallCheck(this, Gltf2Node);
-
-    var _this = _possibleConstructorReturn(this, (Gltf2Node.__proto__ || Object.getPrototypeOf(Gltf2Node)).call(this));
-
-    _this._url = options.url;
-
-    _this._promise = null;
-    _this._resolver = null;
-    _this._rejecter = null;
-    return _this;
-  }
-
-  _createClass(Gltf2Node, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      var _this2 = this;
-
-      var loader = gltfLoaderMap.get(renderer);
-      if (!loader) {
-        loader = new _gltf.Gltf2Loader(renderer);
-        gltfLoaderMap.set(renderer, loader);
-      }
-
-      // Do we have a previously resolved promise? If so clear it.
-      if (!this._resolver && this._promise) {
-        this._promise = null;
-      }
-
-      this._ensurePromise();
-
-      loader.loadFromUrl(this._url).then(function (sceneNode) {
-        _this2.addNode(sceneNode);
-        _this2._resolver(sceneNode.waitForComplete());
-        _this2._resolver = null;
-        _this2._rejecter = null;
-      }).catch(function (err) {
-        _this2._rejecter(err);
-        _this2._resolver = null;
-        _this2._rejecter = null;
-      });
-    }
-  }, {
-    key: '_ensurePromise',
-    value: function _ensurePromise() {
-      var _this3 = this;
-
-      if (!this._promise) {
-        this._promise = new Promise(function (resolve, reject) {
-          _this3._resolver = resolve;
-          _this3._rejecter = reject;
-        });
-      }
-      return this._promise;
-    }
-  }, {
-    key: 'waitForComplete',
-    value: function waitForComplete() {
-      return this._ensurePromise();
-    }
-  }]);
-
-  return Gltf2Node;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/input-renderer.js":
-/*!*************************************!*\
-  !*** ./src/nodes/input-renderer.js ***!
-  \*************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.InputRenderer = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _texture = __webpack_require__(/*! ../core/texture.js */ "./src/core/texture.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var GL = WebGLRenderingContext; // For enums
-
-// Laser texture data, 48x1 RGBA (not premultiplied alpha). This represents a
-// "cross section" of the laser beam with a bright core and a feathered edge.
-// Borrowed from Chromium source code.
-var LASER_TEXTURE_DATA = new Uint8Array([0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0xbf, 0xbf, 0xbf, 0x04, 0xcc, 0xcc, 0xcc, 0x05, 0xdb, 0xdb, 0xdb, 0x07, 0xcc, 0xcc, 0xcc, 0x0a, 0xd8, 0xd8, 0xd8, 0x0d, 0xd2, 0xd2, 0xd2, 0x11, 0xce, 0xce, 0xce, 0x15, 0xce, 0xce, 0xce, 0x1a, 0xce, 0xce, 0xce, 0x1f, 0xcd, 0xcd, 0xcd, 0x24, 0xc8, 0xc8, 0xc8, 0x2a, 0xc9, 0xc9, 0xc9, 0x2f, 0xc9, 0xc9, 0xc9, 0x34, 0xc9, 0xc9, 0xc9, 0x39, 0xc9, 0xc9, 0xc9, 0x3d, 0xc8, 0xc8, 0xc8, 0x41, 0xcb, 0xcb, 0xcb, 0x44, 0xee, 0xee, 0xee, 0x87, 0xfa, 0xfa, 0xfa, 0xc8, 0xf9, 0xf9, 0xf9, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xfa, 0xfa, 0xfa, 0xc9, 0xfa, 0xfa, 0xfa, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xfa, 0xfa, 0xfa, 0xc8, 0xee, 0xee, 0xee, 0x87, 0xcb, 0xcb, 0xcb, 0x44, 0xc8, 0xc8, 0xc8, 0x41, 0xc9, 0xc9, 0xc9, 0x3d, 0xc9, 0xc9, 0xc9, 0x39, 0xc9, 0xc9, 0xc9, 0x34, 0xc9, 0xc9, 0xc9, 0x2f, 0xc8, 0xc8, 0xc8, 0x2a, 0xcd, 0xcd, 0xcd, 0x24, 0xce, 0xce, 0xce, 0x1f, 0xce, 0xce, 0xce, 0x1a, 0xce, 0xce, 0xce, 0x15, 0xd2, 0xd2, 0xd2, 0x11, 0xd8, 0xd8, 0xd8, 0x0d, 0xcc, 0xcc, 0xcc, 0x0a, 0xdb, 0xdb, 0xdb, 0x07, 0xcc, 0xcc, 0xcc, 0x05, 0xbf, 0xbf, 0xbf, 0x04, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x01]);
-
-var LASER_LENGTH = 1.0;
-var LASER_DIAMETER = 0.01;
-var LASER_FADE_END = 0.535;
-var LASER_FADE_POINT = 0.5335;
-var LASER_DEFAULT_COLOR = [1.0, 1.0, 1.0, 0.25];
-
-var CURSOR_RADIUS = 0.004;
-var CURSOR_SHADOW_RADIUS = 0.007;
-var CURSOR_SHADOW_INNER_LUMINANCE = 0.5;
-var CURSOR_SHADOW_OUTER_LUMINANCE = 0.0;
-var CURSOR_SHADOW_INNER_OPACITY = 0.75;
-var CURSOR_SHADOW_OUTER_OPACITY = 0.0;
-var CURSOR_OPACITY = 0.9;
-var CURSOR_SEGMENTS = 16;
-var CURSOR_DEFAULT_COLOR = [1.0, 1.0, 1.0, 1.0];
-var CURSOR_DEFAULT_HIDDEN_COLOR = [0.5, 0.5, 0.5, 0.25];
-
-var DEFAULT_RESET_OPTIONS = {
-  controllers: true,
-  lasers: true,
-  cursors: true
-};
-
-var LaserMaterial = function (_Material) {
-  _inherits(LaserMaterial, _Material);
-
-  function LaserMaterial() {
-    _classCallCheck(this, LaserMaterial);
-
-    var _this = _possibleConstructorReturn(this, (LaserMaterial.__proto__ || Object.getPrototypeOf(LaserMaterial)).call(this));
-
-    _this.renderOrder = _material.RENDER_ORDER.ADDITIVE;
-    _this.state.cullFace = false;
-    _this.state.blend = true;
-    _this.state.blendFuncSrc = GL.ONE;
-    _this.state.blendFuncDst = GL.ONE;
-    _this.state.depthMask = false;
-
-    _this.laser = _this.defineSampler('diffuse');
-    _this.laser.texture = new _texture.DataTexture(LASER_TEXTURE_DATA, 48, 1);
-    _this.laserColor = _this.defineUniform('laserColor', LASER_DEFAULT_COLOR);
-    return _this;
-  }
-
-  _createClass(LaserMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'INPUT_LASER';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    precision mediump float;\n\n    uniform vec4 laserColor;\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    const float fadePoint = ' + LASER_FADE_POINT + ';\n    const float fadeEnd = ' + LASER_FADE_END + ';\n\n    vec4 fragment_main() {\n      vec2 uv = vTexCoord;\n      float front_fade_factor = 1.0 - clamp(1.0 - (uv.y - fadePoint) / (1.0 - fadePoint), 0.0, 1.0);\n      float back_fade_factor = clamp((uv.y - fadePoint) / (fadeEnd - fadePoint), 0.0, 1.0);\n      vec4 color = laserColor * texture2D(diffuse, vTexCoord);\n      float opacity = color.a * front_fade_factor * back_fade_factor;\n      return vec4(color.rgb * opacity, opacity);\n    }';
-    }
-  }]);
-
-  return LaserMaterial;
-}(_material.Material);
-
-var CURSOR_VERTEX_SHADER = '\nattribute vec4 POSITION;\n\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vLuminance = POSITION.z;\n  vOpacity = POSITION.w;\n\n  // Billboarded, constant size vertex transform.\n  vec4 screenPos = proj * view * model * vec4(0.0, 0.0, 0.0, 1.0);\n  screenPos /= screenPos.w;\n  screenPos.xy += POSITION.xy;\n  return screenPos;\n}';
-
-var CURSOR_FRAGMENT_SHADER = '\nprecision mediump float;\n\nuniform vec4 cursorColor;\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 fragment_main() {\n  vec3 color = cursorColor.rgb * vLuminance;\n  float opacity = cursorColor.a * vOpacity;\n  return vec4(color * opacity, opacity);\n}';
-
-// Cursors are drawn as billboards that always face the camera and are rendered
-// as a fixed size no matter how far away they are.
-
-var CursorMaterial = function (_Material2) {
-  _inherits(CursorMaterial, _Material2);
-
-  function CursorMaterial() {
-    _classCallCheck(this, CursorMaterial);
-
-    var _this2 = _possibleConstructorReturn(this, (CursorMaterial.__proto__ || Object.getPrototypeOf(CursorMaterial)).call(this));
-
-    _this2.renderOrder = _material.RENDER_ORDER.ADDITIVE;
-    _this2.state.cullFace = false;
-    _this2.state.blend = true;
-    _this2.state.blendFuncSrc = GL.ONE;
-    _this2.state.depthMask = false;
-
-    _this2.cursorColor = _this2.defineUniform('cursorColor', CURSOR_DEFAULT_COLOR);
-    return _this2;
-  }
-
-  _createClass(CursorMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'INPUT_CURSOR';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return CURSOR_VERTEX_SHADER;
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return CURSOR_FRAGMENT_SHADER;
-    }
-  }]);
-
-  return CursorMaterial;
-}(_material.Material);
-
-var CursorHiddenMaterial = function (_Material3) {
-  _inherits(CursorHiddenMaterial, _Material3);
-
-  function CursorHiddenMaterial() {
-    _classCallCheck(this, CursorHiddenMaterial);
-
-    var _this3 = _possibleConstructorReturn(this, (CursorHiddenMaterial.__proto__ || Object.getPrototypeOf(CursorHiddenMaterial)).call(this));
-
-    _this3.renderOrder = _material.RENDER_ORDER.ADDITIVE;
-    _this3.state.cullFace = false;
-    _this3.state.blend = true;
-    _this3.state.blendFuncSrc = GL.ONE;
-    _this3.state.depthFunc = GL.GEQUAL;
-    _this3.state.depthMask = false;
-
-    _this3.cursorColor = _this3.defineUniform('cursorColor', CURSOR_DEFAULT_HIDDEN_COLOR);
-    return _this3;
-  }
-
-  // TODO: Rename to "program_name"
-
-
-  _createClass(CursorHiddenMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'INPUT_CURSOR_2';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return CURSOR_VERTEX_SHADER;
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return CURSOR_FRAGMENT_SHADER;
-    }
-  }]);
-
-  return CursorHiddenMaterial;
-}(_material.Material);
-
-var InputRenderer = exports.InputRenderer = function (_Node) {
-  _inherits(InputRenderer, _Node);
-
-  function InputRenderer() {
-    _classCallCheck(this, InputRenderer);
-
-    var _this4 = _possibleConstructorReturn(this, (InputRenderer.__proto__ || Object.getPrototypeOf(InputRenderer)).call(this));
-
-    _this4._maxInputElements = 32;
-
-    _this4._controllers = [];
-    _this4._controllerNode = null;
-    _this4._controllerNodeHandedness = null;
-    _this4._lasers = null;
-    _this4._cursors = null;
-
-    _this4._activeControllers = 0;
-    _this4._activeLasers = 0;
-    _this4._activeCursors = 0;
-    return _this4;
-  }
-
-  _createClass(InputRenderer, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      this._controllers = [];
-      this._controllerNode = null;
-      this._controllerNodeHandedness = null;
-      this._lasers = null;
-      this._cursors = null;
-
-      this._activeControllers = 0;
-      this._activeLasers = 0;
-      this._activeCursors = 0;
-    }
-  }, {
-    key: 'setControllerMesh',
-    value: function setControllerMesh(controllerNode) {
-      var handedness = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'right';
-
-      this._controllerNode = controllerNode;
-      this._controllerNode.visible = false;
-      // FIXME: Temporary fix to initialize for cloning.
-      this.addNode(this._controllerNode);
-      this._controllerNodeHandedness = handedness;
-    }
-  }, {
-    key: 'addController',
-    value: function addController(gripMatrix) {
-      if (!this._controllerNode) {
-        return;
-      }
-
-      var controller = null;
-      if (this._activeControllers < this._controllers.length) {
-        controller = this._controllers[this._activeControllers];
-      } else {
-        controller = this._controllerNode.clone();
-        this.addNode(controller);
-        this._controllers.push(controller);
-      }
-      this._activeControllers = (this._activeControllers + 1) % this._maxInputElements;
-
-      controller.matrix = gripMatrix;
-      controller.visible = true;
-    }
-  }, {
-    key: 'addLaserPointer',
-    value: function addLaserPointer(targetRay) {
-      // Create the laser pointer mesh if needed.
-      if (!this._lasers && this._renderer) {
-        this._lasers = [this._createLaserMesh()];
-        this.addNode(this._lasers[0]);
-      }
-
-      var laser = null;
-      if (this._activeLasers < this._lasers.length) {
-        laser = this._lasers[this._activeLasers];
-      } else {
-        laser = this._lasers[0].clone();
-        this.addNode(laser);
-        this._lasers.push(laser);
-      }
-      this._activeLasers = (this._activeLasers + 1) % this._maxInputElements;
-
-      laser.matrix = targetRay.transformMatrix;
-      laser.visible = true;
-    }
-  }, {
-    key: 'addCursor',
-    value: function addCursor(cursorPos) {
-      // Create the cursor mesh if needed.
-      if (!this._cursors && this._renderer) {
-        this._cursors = [this._createCursorMesh()];
-        this.addNode(this._cursors[0]);
-      }
-
-      var cursor = null;
-      if (this._activeCursors < this._cursors.length) {
-        cursor = this._cursors[this._activeCursors];
-      } else {
-        cursor = this._cursors[0].clone();
-        this.addNode(cursor);
-        this._cursors.push(cursor);
-      }
-      this._activeCursors = (this._activeCursors + 1) % this._maxInputElements;
-
-      cursor.translation = cursorPos;
-      cursor.visible = true;
-    }
-  }, {
-    key: 'reset',
-    value: function reset(options) {
-      if (!options) {
-        options = DEFAULT_RESET_OPTIONS;
-      }
-      if (this._controllers && options.controllers) {
-        var _iteratorNormalCompletion = true;
-        var _didIteratorError = false;
-        var _iteratorError = undefined;
-
-        try {
-          for (var _iterator = this._controllers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-            var controller = _step.value;
-
-            controller.visible = false;
-          }
-        } catch (err) {
-          _didIteratorError = true;
-          _iteratorError = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion && _iterator.return) {
-              _iterator.return();
-            }
-          } finally {
-            if (_didIteratorError) {
-              throw _iteratorError;
-            }
-          }
-        }
-
-        this._activeControllers = 0;
-      }
-      if (this._lasers && options.lasers) {
-        var _iteratorNormalCompletion2 = true;
-        var _didIteratorError2 = false;
-        var _iteratorError2 = undefined;
-
-        try {
-          for (var _iterator2 = this._lasers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-            var laser = _step2.value;
-
-            laser.visible = false;
-          }
-        } catch (err) {
-          _didIteratorError2 = true;
-          _iteratorError2 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion2 && _iterator2.return) {
-              _iterator2.return();
-            }
-          } finally {
-            if (_didIteratorError2) {
-              throw _iteratorError2;
-            }
-          }
-        }
-
-        this._activeLasers = 0;
-      }
-      if (this._cursors && options.cursors) {
-        var _iteratorNormalCompletion3 = true;
-        var _didIteratorError3 = false;
-        var _iteratorError3 = undefined;
-
-        try {
-          for (var _iterator3 = this._cursors[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
-            var cursor = _step3.value;
-
-            cursor.visible = false;
-          }
-        } catch (err) {
-          _didIteratorError3 = true;
-          _iteratorError3 = err;
-        } finally {
-          try {
-            if (!_iteratorNormalCompletion3 && _iterator3.return) {
-              _iterator3.return();
-            }
-          } finally {
-            if (_didIteratorError3) {
-              throw _iteratorError3;
-            }
-          }
-        }
-
-        this._activeCursors = 0;
-      }
-    }
-  }, {
-    key: '_createLaserMesh',
-    value: function _createLaserMesh() {
-      var gl = this._renderer._gl;
-
-      var lr = LASER_DIAMETER * 0.5;
-      var ll = LASER_LENGTH;
-
-      // Laser is rendered as cross-shaped beam
-      var laserVerts = [
-      // X    Y   Z    U    V
-      0.0, lr, 0.0, 0.0, 1.0, 0.0, lr, -ll, 0.0, 0.0, 0.0, -lr, 0.0, 1.0, 1.0, 0.0, -lr, -ll, 1.0, 0.0, lr, 0.0, 0.0, 0.0, 1.0, lr, 0.0, -ll, 0.0, 0.0, -lr, 0.0, 0.0, 1.0, 1.0, -lr, 0.0, -ll, 1.0, 0.0, 0.0, -lr, 0.0, 0.0, 1.0, 0.0, -lr, -ll, 0.0, 0.0, 0.0, lr, 0.0, 1.0, 1.0, 0.0, lr, -ll, 1.0, 0.0, -lr, 0.0, 0.0, 0.0, 1.0, -lr, 0.0, -ll, 0.0, 0.0, lr, 0.0, 0.0, 1.0, 1.0, lr, 0.0, -ll, 1.0, 0.0];
-      var laserIndices = [0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15, 14];
-
-      var laserVertexBuffer = this._renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(laserVerts));
-      var laserIndexBuffer = this._renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(laserIndices));
-
-      var laserIndexCount = laserIndices.length;
-
-      var laserAttribs = [new _primitive.PrimitiveAttribute('POSITION', laserVertexBuffer, 3, gl.FLOAT, 20, 0), new _primitive.PrimitiveAttribute('TEXCOORD_0', laserVertexBuffer, 2, gl.FLOAT, 20, 12)];
-
-      var laserPrimitive = new _primitive.Primitive(laserAttribs, laserIndexCount);
-      laserPrimitive.setIndexBuffer(laserIndexBuffer);
-
-      var laserMaterial = new LaserMaterial();
-
-      var laserRenderPrimitive = this._renderer.createRenderPrimitive(laserPrimitive, laserMaterial);
-      var meshNode = new _node.Node();
-      meshNode.addRenderPrimitive(laserRenderPrimitive);
-      return meshNode;
-    }
-  }, {
-    key: '_createCursorMesh',
-    value: function _createCursorMesh() {
-      var gl = this._renderer._gl;
-
-      // Cursor is a circular white dot with a dark "shadow" skirt around the edge
-      // that fades from black to transparent as it moves out from the center.
-      // Cursor verts are packed as [X, Y, Luminance, Opacity]
-      var cursorVerts = [];
-      var cursorIndices = [];
-
-      var segRad = 2.0 * Math.PI / CURSOR_SEGMENTS;
-
-      // Cursor center
-      for (var i = 0; i < CURSOR_SEGMENTS; ++i) {
-        var rad = i * segRad;
-        var x = Math.cos(rad);
-        var y = Math.sin(rad);
-        cursorVerts.push(x * CURSOR_RADIUS, y * CURSOR_RADIUS, 1.0, CURSOR_OPACITY);
-
-        if (i > 1) {
-          cursorIndices.push(0, i - 1, i);
-        }
-      }
-
-      var indexOffset = CURSOR_SEGMENTS;
-
-      // Cursor Skirt
-      for (var _i = 0; _i < CURSOR_SEGMENTS; ++_i) {
-        var _rad = _i * segRad;
-        var _x2 = Math.cos(_rad);
-        var _y = Math.sin(_rad);
-        cursorVerts.push(_x2 * CURSOR_RADIUS, _y * CURSOR_RADIUS, CURSOR_SHADOW_INNER_LUMINANCE, CURSOR_SHADOW_INNER_OPACITY);
-        cursorVerts.push(_x2 * CURSOR_SHADOW_RADIUS, _y * CURSOR_SHADOW_RADIUS, CURSOR_SHADOW_OUTER_LUMINANCE, CURSOR_SHADOW_OUTER_OPACITY);
-
-        if (_i > 0) {
-          var _idx = indexOffset + _i * 2;
-          cursorIndices.push(_idx - 2, _idx - 1, _idx);
-          cursorIndices.push(_idx - 1, _idx + 1, _idx);
-        }
-      }
-
-      var idx = indexOffset + CURSOR_SEGMENTS * 2;
-      cursorIndices.push(idx - 2, idx - 1, indexOffset);
-      cursorIndices.push(idx - 1, indexOffset + 1, indexOffset);
-
-      var cursorVertexBuffer = this._renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(cursorVerts));
-      var cursorIndexBuffer = this._renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cursorIndices));
-
-      var cursorIndexCount = cursorIndices.length;
-
-      var cursorAttribs = [new _primitive.PrimitiveAttribute('POSITION', cursorVertexBuffer, 4, gl.FLOAT, 16, 0)];
-
-      var cursorPrimitive = new _primitive.Primitive(cursorAttribs, cursorIndexCount);
-      cursorPrimitive.setIndexBuffer(cursorIndexBuffer);
-
-      var cursorMaterial = new CursorMaterial();
-      var cursorHiddenMaterial = new CursorHiddenMaterial();
-
-      // Cursor renders two parts: The bright opaque cursor for areas where it's
-      // not obscured and a more transparent, darker version for areas where it's
-      // behind another object.
-      var cursorRenderPrimitive = this._renderer.createRenderPrimitive(cursorPrimitive, cursorMaterial);
-      var cursorHiddenRenderPrimitive = this._renderer.createRenderPrimitive(cursorPrimitive, cursorHiddenMaterial);
-      var meshNode = new _node.Node();
-      meshNode.addRenderPrimitive(cursorRenderPrimitive);
-      meshNode.addRenderPrimitive(cursorHiddenRenderPrimitive);
-      return meshNode;
-    }
-  }]);
-
-  return InputRenderer;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/seven-segment-text.js":
-/*!*****************************************!*\
-  !*** ./src/nodes/seven-segment-text.js ***!
-  \*****************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.SevenSegmentText = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-Renders simple text using a seven-segment LED style pattern. Only really good
-for numbers and a limited number of other characters.
-*/
-
-var TEXT_KERNING = 2.0;
-
-var SevenSegmentMaterial = function (_Material) {
-  _inherits(SevenSegmentMaterial, _Material);
-
-  function SevenSegmentMaterial() {
-    _classCallCheck(this, SevenSegmentMaterial);
-
-    return _possibleConstructorReturn(this, (SevenSegmentMaterial.__proto__ || Object.getPrototypeOf(SevenSegmentMaterial)).apply(this, arguments));
-  }
-
-  _createClass(SevenSegmentMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'SEVEN_SEGMENT_TEXT';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 0.0, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    precision mediump float;\n    const vec4 color = vec4(0.0, 1.0, 0.0, 1.0);\n\n    vec4 fragment_main() {\n      return color;\n    }';
-    }
-  }]);
-
-  return SevenSegmentMaterial;
-}(_material.Material);
-
-var SevenSegmentText = exports.SevenSegmentText = function (_Node) {
-  _inherits(SevenSegmentText, _Node);
-
-  function SevenSegmentText() {
-    _classCallCheck(this, SevenSegmentText);
-
-    var _this2 = _possibleConstructorReturn(this, (SevenSegmentText.__proto__ || Object.getPrototypeOf(SevenSegmentText)).call(this));
-
-    _this2._text = '';
-    _this2._charNodes = [];
-    return _this2;
-  }
-
-  _createClass(SevenSegmentText, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      this.clearNodes();
-      this._charNodes = [];
-
-      var vertices = [];
-      var segmentIndices = {};
-      var indices = [];
-
-      var width = 0.5;
-      var thickness = 0.25;
-
-      function defineSegment(id, left, top, right, bottom) {
-        var idx = vertices.length / 2;
-        vertices.push(left, top, right, top, right, bottom, left, bottom);
-
-        segmentIndices[id] = [idx, idx + 2, idx + 1, idx, idx + 3, idx + 2];
-      }
-
-      var characters = {};
-      function defineCharacter(c, segments) {
-        var character = {
-          character: c,
-          offset: indices.length * 2,
-          count: 0
-        };
-
-        for (var i = 0; i < segments.length; ++i) {
-          var idx = segments[i];
-          var segment = segmentIndices[idx];
-          character.count += segment.length;
-          indices.push.apply(indices, _toConsumableArray(segment));
-        }
-
-        characters[c] = character;
-      }
-
-      /* Segment layout is as follows:
-       |-0-|
-      3   4
-      |-1-|
-      5   6
-      |-2-|
-       */
-
-      defineSegment(0, -1, 1, width, 1 - thickness);
-      defineSegment(1, -1, thickness * 0.5, width, -thickness * 0.5);
-      defineSegment(2, -1, -1 + thickness, width, -1);
-      defineSegment(3, -1, 1, -1 + thickness, -thickness * 0.5);
-      defineSegment(4, width - thickness, 1, width, -thickness * 0.5);
-      defineSegment(5, -1, thickness * 0.5, -1 + thickness, -1);
-      defineSegment(6, width - thickness, thickness * 0.5, width, -1);
-
-      defineCharacter('0', [0, 2, 3, 4, 5, 6]);
-      defineCharacter('1', [4, 6]);
-      defineCharacter('2', [0, 1, 2, 4, 5]);
-      defineCharacter('3', [0, 1, 2, 4, 6]);
-      defineCharacter('4', [1, 3, 4, 6]);
-      defineCharacter('5', [0, 1, 2, 3, 6]);
-      defineCharacter('6', [0, 1, 2, 3, 5, 6]);
-      defineCharacter('7', [0, 4, 6]);
-      defineCharacter('8', [0, 1, 2, 3, 4, 5, 6]);
-      defineCharacter('9', [0, 1, 2, 3, 4, 6]);
-      defineCharacter('A', [0, 1, 3, 4, 5, 6]);
-      defineCharacter('B', [1, 2, 3, 5, 6]);
-      defineCharacter('C', [0, 2, 3, 5]);
-      defineCharacter('D', [1, 2, 4, 5, 6]);
-      defineCharacter('E', [0, 1, 2, 4, 6]);
-      defineCharacter('F', [0, 1, 3, 5]);
-      defineCharacter('P', [0, 1, 3, 4, 5]);
-      defineCharacter('-', [1]);
-      defineCharacter(' ', []);
-      defineCharacter('_', [2]); // Used for undefined characters
-
-      var gl = renderer.gl;
-      var vertexBuffer = renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(vertices));
-      var indexBuffer = renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));
-
-      var vertexAttribs = [new _primitive.PrimitiveAttribute('POSITION', vertexBuffer, 2, gl.FLOAT, 8, 0)];
-
-      var primitive = new _primitive.Primitive(vertexAttribs, indices.length);
-      primitive.setIndexBuffer(indexBuffer);
-
-      var material = new SevenSegmentMaterial();
-
-      this._charPrimitives = {};
-      for (var char in characters) {
-        var charDef = characters[char];
-        primitive.elementCount = charDef.count;
-        primitive.indexByteOffset = charDef.offset;
-        this._charPrimitives[char] = renderer.createRenderPrimitive(primitive, material);
-      }
-
-      this.text = this._text;
-    }
-  }, {
-    key: 'text',
-    get: function get() {
-      return this._text;
-    },
-    set: function set(value) {
-      this._text = value;
-
-      var i = 0;
-      var charPrimitive = null;
-      for (; i < value.length; ++i) {
-        if (value[i] in this._charPrimitives) {
-          charPrimitive = this._charPrimitives[value[i]];
-        } else {
-          charPrimitive = this._charPrimitives['_'];
-        }
-
-        if (this._charNodes.length <= i) {
-          var node = new _node.Node();
-          node.addRenderPrimitive(charPrimitive);
-          var offset = i * TEXT_KERNING;
-          node.translation = [offset, 0, 0];
-          this._charNodes.push(node);
-          this.addNode(node);
-        } else {
-          // This is sort of an abuse of how these things are expected to work,
-          // but it's the cheapest thing I could think of that didn't break the
-          // world.
-          this._charNodes[i].clearRenderPrimitives();
-          this._charNodes[i].addRenderPrimitive(charPrimitive);
-          this._charNodes[i].visible = true;
-        }
-      }
-
-      // If there's any nodes left over make them invisible
-      for (; i < this._charNodes.length; ++i) {
-        this._charNodes[i].visible = false;
-      }
-    }
-  }]);
-
-  return SevenSegmentText;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/skybox.js":
-/*!*****************************!*\
-  !*** ./src/nodes/skybox.js ***!
-  \*****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.SkyboxNode = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _texture = __webpack_require__(/*! ../core/texture.js */ "./src/core/texture.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-Node for displaying 360 equirect images as a skybox.
-*/
-
-var GL = WebGLRenderingContext; // For enums
-
-var SkyboxMaterial = function (_Material) {
-  _inherits(SkyboxMaterial, _Material);
-
-  function SkyboxMaterial() {
-    _classCallCheck(this, SkyboxMaterial);
-
-    var _this = _possibleConstructorReturn(this, (SkyboxMaterial.__proto__ || Object.getPrototypeOf(SkyboxMaterial)).call(this));
-
-    _this.renderOrder = _material.RENDER_ORDER.SKY;
-    _this.state.depthFunc = GL.LEQUAL;
-    _this.state.depthMask = false;
-
-    _this.image = _this.defineSampler('diffuse');
-
-    _this.texCoordScaleOffset = _this.defineUniform('texCoordScaleOffset', [1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0], 4);
-    return _this;
-  }
-
-  _createClass(SkyboxMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'SKYBOX';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      // Drop the translation portion of the view matrix\n      view[3].xyz = vec3(0.0, 0.0, 0.0);\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n\n      // Returning the W component for both Z and W forces the geometry depth to\n      // the far plane. When combined with a depth func of LEQUAL this makes the\n      // sky write to any depth fragment that has not been written to yet.\n      return out_vec.xyww;\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }';
-    }
-  }]);
-
-  return SkyboxMaterial;
-}(_material.Material);
-
-var SkyboxNode = exports.SkyboxNode = function (_Node) {
-  _inherits(SkyboxNode, _Node);
-
-  function SkyboxNode(options) {
-    _classCallCheck(this, SkyboxNode);
-
-    var _this2 = _possibleConstructorReturn(this, (SkyboxNode.__proto__ || Object.getPrototypeOf(SkyboxNode)).call(this));
-
-    _this2._url = options.url;
-    _this2._displayMode = options.displayMode || 'mono';
-    _this2._rotationY = options.rotationY || 0;
-    return _this2;
-  }
-
-  _createClass(SkyboxNode, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      var vertices = [];
-      var indices = [];
-
-      var latSegments = 40;
-      var lonSegments = 40;
-
-      // Create the vertices/indices
-      for (var i = 0; i <= latSegments; ++i) {
-        var theta = i * Math.PI / latSegments;
-        var sinTheta = Math.sin(theta);
-        var cosTheta = Math.cos(theta);
-
-        var idxOffsetA = i * (lonSegments + 1);
-        var idxOffsetB = (i + 1) * (lonSegments + 1);
-
-        for (var j = 0; j <= lonSegments; ++j) {
-          var phi = j * 2 * Math.PI / lonSegments + this._rotationY;
-          var x = Math.sin(phi) * sinTheta;
-          var y = cosTheta;
-          var z = -Math.cos(phi) * sinTheta;
-          var u = j / lonSegments;
-          var v = i / latSegments;
-
-          // Vertex shader will force the geometry to the far plane, so the
-          // radius of the sphere is immaterial.
-          vertices.push(x, y, z, u, v);
-
-          if (i < latSegments && j < lonSegments) {
-            var idxA = idxOffsetA + j;
-            var idxB = idxOffsetB + j;
-
-            indices.push(idxA, idxB, idxA + 1, idxB, idxB + 1, idxA + 1);
-          }
-        }
-      }
-
-      var vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices));
-      var indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));
-
-      var attribs = [new _primitive.PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0), new _primitive.PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12)];
-
-      var primitive = new _primitive.Primitive(attribs, indices.length);
-      primitive.setIndexBuffer(indexBuffer);
-
-      var material = new SkyboxMaterial();
-      material.image.texture = new _texture.UrlTexture(this._url);
-
-      switch (this._displayMode) {
-        case 'mono':
-          material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0];
-          break;
-        case 'stereoTopBottom':
-          material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.5];
-          break;
-        case 'stereoLeftRight':
-          material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0, 0.5, 1.0, 0.5, 0.0];
-          break;
-      }
-
-      var renderPrimitive = renderer.createRenderPrimitive(primitive, material);
-      this.addRenderPrimitive(renderPrimitive);
-    }
-  }]);
-
-  return SkyboxNode;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/stats-viewer.js":
-/*!***********************************!*\
-  !*** ./src/nodes/stats-viewer.js ***!
-  \***********************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.StatsViewer = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _sevenSegmentText = __webpack_require__(/*! ./seven-segment-text.js */ "./src/nodes/seven-segment-text.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-Heavily inspired by Mr. Doobs stats.js, this FPS counter is rendered completely
-with WebGL, allowing it to be shown in cases where overlaid HTML elements aren't
-usable (like WebXR), or if you want the FPS counter to be rendered as part of
-your scene.
-*/
-
-var SEGMENTS = 30;
-var MAX_FPS = 90;
-
-var StatsMaterial = function (_Material) {
-  _inherits(StatsMaterial, _Material);
-
-  function StatsMaterial() {
-    _classCallCheck(this, StatsMaterial);
-
-    return _possibleConstructorReturn(this, (StatsMaterial.__proto__ || Object.getPrototypeOf(StatsMaterial)).apply(this, arguments));
-  }
-
-  _createClass(StatsMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'STATS_VIEWER';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    attribute vec3 POSITION;\n    attribute vec3 COLOR_0;\n    varying vec4 vColor;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vColor = vec4(COLOR_0, 1.0);\n      return proj * view * model * vec4(POSITION, 1.0);\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    precision mediump float;\n    varying vec4 vColor;\n\n    vec4 fragment_main() {\n      return vColor;\n    }';
-    }
-  }]);
-
-  return StatsMaterial;
-}(_material.Material);
-
-function segmentToX(i) {
-  return 0.9 / SEGMENTS * i - 0.45;
-}
-
-function fpsToY(value) {
-  return Math.min(value, MAX_FPS) * (0.7 / MAX_FPS) - 0.45;
-}
-
-function fpsToRGB(value) {
-  return {
-    r: Math.max(0.0, Math.min(1.0, 1.0 - value / 60)),
-    g: Math.max(0.0, Math.min(1.0, (value - 15) / (MAX_FPS - 15))),
-    b: Math.max(0.0, Math.min(1.0, (value - 15) / (MAX_FPS - 15)))
-  };
-}
-
-var now = window.performance && performance.now ? performance.now.bind(performance) : Date.now;
-
-var StatsViewer = exports.StatsViewer = function (_Node) {
-  _inherits(StatsViewer, _Node);
-
-  function StatsViewer() {
-    _classCallCheck(this, StatsViewer);
-
-    var _this2 = _possibleConstructorReturn(this, (StatsViewer.__proto__ || Object.getPrototypeOf(StatsViewer)).call(this));
-
-    _this2._performanceMonitoring = false;
-
-    _this2._startTime = now();
-    _this2._prevFrameTime = _this2._startTime;
-    _this2._prevGraphUpdateTime = _this2._startTime;
-    _this2._frames = 0;
-    _this2._fpsAverage = 0;
-    _this2._fpsMin = 0;
-    _this2._fpsStep = _this2._performanceMonitoring ? 1000 : 250;
-    _this2._lastSegment = 0;
-
-    _this2._fpsVertexBuffer = null;
-    _this2._fpsRenderPrimitive = null;
-    _this2._fpsNode = null;
-
-    _this2._sevenSegmentNode = new _sevenSegmentText.SevenSegmentText();
-    // Hard coded because it doesn't change:
-    // Scale by 0.075 in X and Y
-    // Translate into upper left corner w/ z = 0.02
-    _this2._sevenSegmentNode.matrix = new Float32Array([0.075, 0, 0, 0, 0, 0.075, 0, 0, 0, 0, 1, 0, -0.3625, 0.3625, 0.02, 1]);
-    return _this2;
-  }
-
-  _createClass(StatsViewer, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      this.clearNodes();
-
-      var gl = renderer.gl;
-
-      var fpsVerts = [];
-      var fpsIndices = [];
-
-      // Graph geometry
-      for (var i = 0; i < SEGMENTS; ++i) {
-        // Bar top
-        fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0);
-        fpsVerts.push(segmentToX(i + 1), fpsToY(0), 0.02, 0.0, 1.0, 1.0);
-
-        // Bar bottom
-        fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0);
-        fpsVerts.push(segmentToX(i + 1), fpsToY(0), 0.02, 0.0, 1.0, 1.0);
-
-        var idx = i * 4;
-        fpsIndices.push(idx, idx + 3, idx + 1, idx + 3, idx, idx + 2);
-      }
-
-      function addBGSquare(left, bottom, right, top, z, r, g, b) {
-        var idx = fpsVerts.length / 6;
-
-        fpsVerts.push(left, bottom, z, r, g, b);
-        fpsVerts.push(right, top, z, r, g, b);
-        fpsVerts.push(left, top, z, r, g, b);
-        fpsVerts.push(right, bottom, z, r, g, b);
-
-        fpsIndices.push(idx, idx + 1, idx + 2, idx, idx + 3, idx + 1);
-      }
-
-      // Panel Background
-      addBGSquare(-0.5, -0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.125);
-
-      // FPS Background
-      addBGSquare(-0.45, -0.45, 0.45, 0.25, 0.01, 0.0, 0.0, 0.4);
-
-      // 30 FPS line
-      addBGSquare(-0.45, fpsToY(30), 0.45, fpsToY(32), 0.015, 0.5, 0.0, 0.5);
-
-      // 60 FPS line
-      addBGSquare(-0.45, fpsToY(60), 0.45, fpsToY(62), 0.015, 0.2, 0.0, 0.75);
-
-      this._fpsVertexBuffer = renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(fpsVerts), gl.DYNAMIC_DRAW);
-      var fpsIndexBuffer = renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(fpsIndices));
-
-      var fpsAttribs = [new _primitive.PrimitiveAttribute('POSITION', this._fpsVertexBuffer, 3, gl.FLOAT, 24, 0), new _primitive.PrimitiveAttribute('COLOR_0', this._fpsVertexBuffer, 3, gl.FLOAT, 24, 12)];
-
-      var fpsPrimitive = new _primitive.Primitive(fpsAttribs, fpsIndices.length);
-      fpsPrimitive.setIndexBuffer(fpsIndexBuffer);
-      fpsPrimitive.setBounds([-0.5, -0.5, 0.0], [0.5, 0.5, 0.015]);
-
-      this._fpsRenderPrimitive = renderer.createRenderPrimitive(fpsPrimitive, new StatsMaterial());
-      this._fpsNode = new _node.Node();
-      this._fpsNode.addRenderPrimitive(this._fpsRenderPrimitive);
-
-      this.addNode(this._fpsNode);
-      this.addNode(this._sevenSegmentNode);
-    }
-  }, {
-    key: 'begin',
-    value: function begin() {
-      this._startTime = now();
-    }
-  }, {
-    key: 'end',
-    value: function end() {
-      var time = now();
-
-      var frameFps = 1000 / (time - this._prevFrameTime);
-      this._prevFrameTime = time;
-      this._fpsMin = this._frames ? Math.min(this._fpsMin, frameFps) : frameFps;
-      this._frames++;
-
-      if (time > this._prevGraphUpdateTime + this._fpsStep) {
-        var intervalTime = time - this._prevGraphUpdateTime;
-        this._fpsAverage = Math.round(1000 / (intervalTime / this._frames));
-
-        // Draw both average and minimum FPS for this period
-        // so that dropped frames are more clearly visible.
-        this._updateGraph(this._fpsMin, this._fpsAverage);
-        if (this._performanceMonitoring) {
-          console.log('Average FPS: ' + this._fpsAverage + ' Min FPS: ' + this._fpsMin);
-        }
-
-        this._prevGraphUpdateTime = time;
-        this._frames = 0;
-        this._fpsMin = 0;
-      }
-    }
-  }, {
-    key: '_updateGraph',
-    value: function _updateGraph(valueLow, valueHigh) {
-      var color = fpsToRGB(valueLow);
-      // Draw a range from the low to high value. Artificially widen the
-      // range a bit to ensure that near-equal values still remain
-      // visible - the logic here should match that used by the
-      // "60 FPS line" setup below. Hitting 60fps consistently will
-      // keep the top half of the 60fps background line visible.
-      var y0 = fpsToY(valueLow - 1);
-      var y1 = fpsToY(valueHigh + 1);
-
-      // Update the current segment with the new FPS value
-      var updateVerts = [segmentToX(this._lastSegment), y1, 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment + 1), y1, 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment), y0, 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment + 1), y0, 0.02, color.r, color.g, color.b];
-
-      // Re-shape the next segment into the green "progress" line
-      color.r = 0.2;
-      color.g = 1.0;
-      color.b = 0.2;
-
-      if (this._lastSegment == SEGMENTS - 1) {
-        // If we're updating the last segment we need to do two bufferSubDatas
-        // to update the segment and turn the first segment into the progress line.
-        this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts), this._lastSegment * 24 * 4);
-        updateVerts = [segmentToX(0), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b, segmentToX(.25), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b, segmentToX(0), fpsToY(0), 0.02, color.r, color.g, color.b, segmentToX(.25), fpsToY(0), 0.02, color.r, color.g, color.b];
-        this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts), 0);
-      } else {
-        updateVerts.push(segmentToX(this._lastSegment + 1), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment + 1.25), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment + 1), fpsToY(0), 0.02, color.r, color.g, color.b, segmentToX(this._lastSegment + 1.25), fpsToY(0), 0.02, color.r, color.g, color.b);
-        this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts), this._lastSegment * 24 * 4);
-      }
-
-      this._lastSegment = (this._lastSegment + 1) % SEGMENTS;
-
-      this._sevenSegmentNode.text = this._fpsAverage + ' FP5';
-    }
-  }, {
-    key: 'performanceMonitoring',
-    get: function get() {
-      return this._performanceMonitoring;
-    },
-    set: function set(value) {
-      this._performanceMonitoring = value;
-      this._fpsStep = value ? 1000 : 250;
-    }
-  }]);
-
-  return StatsViewer;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/nodes/video.js":
-/*!****************************!*\
-  !*** ./src/nodes/video.js ***!
-  \****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.VideoNode = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _material = __webpack_require__(/*! ../core/material.js */ "./src/core/material.js");
-
-var _primitive = __webpack_require__(/*! ../core/primitive.js */ "./src/core/primitive.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _texture = __webpack_require__(/*! ../core/texture.js */ "./src/core/texture.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-Node for displaying 2D or stereo videos on a quad.
-*/
-
-var GL = WebGLRenderingContext; // For enums
-
-var VideoMaterial = function (_Material) {
-  _inherits(VideoMaterial, _Material);
-
-  function VideoMaterial() {
-    _classCallCheck(this, VideoMaterial);
-
-    var _this = _possibleConstructorReturn(this, (VideoMaterial.__proto__ || Object.getPrototypeOf(VideoMaterial)).call(this));
-
-    _this.image = _this.defineSampler('diffuse');
-
-    _this.texCoordScaleOffset = _this.defineUniform('texCoordScaleOffset', [1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0], 4);
-    return _this;
-  }
-
-  _createClass(VideoMaterial, [{
-    key: 'materialName',
-    get: function get() {
-      return 'VIDEO_PLAYER';
-    }
-  }, {
-    key: 'vertexSource',
-    get: function get() {
-      return '\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n      return out_vec;\n    }';
-    }
-  }, {
-    key: 'fragmentSource',
-    get: function get() {
-      return '\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }';
-    }
-  }]);
-
-  return VideoMaterial;
-}(_material.Material);
-
-var VideoNode = exports.VideoNode = function (_Node) {
-  _inherits(VideoNode, _Node);
-
-  function VideoNode(options) {
-    _classCallCheck(this, VideoNode);
-
-    var _this2 = _possibleConstructorReturn(this, (VideoNode.__proto__ || Object.getPrototypeOf(VideoNode)).call(this));
-
-    _this2._video = options.video;
-    _this2._displayMode = options.displayMode || 'mono';
-
-    _this2._video_texture = new _texture.VideoTexture(_this2._video);
-    return _this2;
-  }
-
-  _createClass(VideoNode, [{
-    key: 'onRendererChanged',
-    value: function onRendererChanged(renderer) {
-      var vertices = [-1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0];
-      var indices = [0, 2, 1, 0, 3, 2];
-
-      var vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices));
-      var indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));
-
-      var attribs = [new _primitive.PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0), new _primitive.PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12)];
-
-      var primitive = new _primitive.Primitive(attribs, indices.length);
-      primitive.setIndexBuffer(indexBuffer);
-      primitive.setBounds([-1.0, -1.0, 0.0], [1.0, 1.0, 0.015]);
-
-      var material = new VideoMaterial();
-      material.image.texture = this._video_texture;
-
-      switch (this._displayMode) {
-        case 'mono':
-          material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0];
-          break;
-        case 'stereoTopBottom':
-          material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0, 1.0, 0.5, 0.0, 0.5];
-          break;
-        case 'stereoLeftRight':
-          material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0, 0.5, 1.0, 0.5, 0.0];
-          break;
-      }
-
-      var renderPrimitive = renderer.createRenderPrimitive(primitive, material);
-      this.addRenderPrimitive(renderPrimitive);
-    }
-  }, {
-    key: 'aspectRatio',
-    get: function get() {
-      var width = this._video.videoWidth;
-      var height = this._video.videoHeight;
-
-      switch (this._displayMode) {
-        case 'stereoTopBottom':
-          height *= 0.5;break;
-        case 'stereoLeftRight':
-          width *= 0.5;break;
-      }
-
-      if (!height || !width) {
-        return 1;
-      }
-
-      return width / height;
-    }
-  }]);
-
-  return VideoNode;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/scenes/scene.js":
-/*!*****************************!*\
-  !*** ./src/scenes/scene.js ***!
-  \*****************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.Scene = exports.WebXRView = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _renderer = __webpack_require__(/*! ../core/renderer.js */ "./src/core/renderer.js");
-
-var _inputRenderer = __webpack_require__(/*! ../nodes/input-renderer.js */ "./src/nodes/input-renderer.js");
-
-var _statsViewer = __webpack_require__(/*! ../nodes/stats-viewer.js */ "./src/nodes/stats-viewer.js");
-
-var _node = __webpack_require__(/*! ../core/node.js */ "./src/core/node.js");
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var WebXRView = exports.WebXRView = function (_RenderView) {
-  _inherits(WebXRView, _RenderView);
-
-  function WebXRView(view, layer) {
-    _classCallCheck(this, WebXRView);
-
-    return _possibleConstructorReturn(this, (WebXRView.__proto__ || Object.getPrototypeOf(WebXRView)).call(this, view ? view.projectionMatrix : null, view ? view.viewMatrix : null, layer && view ? layer.getViewport(view) : null, view ? view.eye : 'left'));
-  }
-
-  return WebXRView;
-}(_renderer.RenderView);
-
-var Scene = exports.Scene = function (_Node) {
-  _inherits(Scene, _Node);
-
-  function Scene() {
-    _classCallCheck(this, Scene);
-
-    var _this2 = _possibleConstructorReturn(this, (Scene.__proto__ || Object.getPrototypeOf(Scene)).call(this));
-
-    _this2._timestamp = -1;
-    _this2._frameDelta = 0;
-    _this2._statsStanding = false;
-    _this2._stats = null;
-    _this2._statsEnabled = false;
-    _this2.enableStats(true); // Ensure the stats are added correctly by default.
-
-    _this2._inputRenderer = null;
-    _this2._resetInputEndFrame = true;
-
-    _this2._lastTimestamp = 0;
-
-    _this2._hoverFrame = 0;
-    _this2._hoveredNodes = [];
-
-    _this2.clear = true;
-    return _this2;
-  }
-
-  _createClass(Scene, [{
-    key: 'setRenderer',
-    value: function setRenderer(renderer) {
-      this._setRenderer(renderer);
-    }
-  }, {
-    key: 'loseRenderer',
-    value: function loseRenderer() {
-      if (this._renderer) {
-        this._stats = null;
-        this._renderer = null;
-        this._inputRenderer = null;
-      }
-    }
-  }, {
-    key: 'updateInputSources',
-
-
-    // Helper function that automatically adds the appropriate visual elements for
-    // all input sources.
-    value: function updateInputSources(frame, frameOfRef) {
-      // FIXME: Check for the existence of the API first. This check should be
-      // removed once the input API is part of the official spec.
-      if (!frame.session.getInputSources) {
-        return;
-      }
-
-      var inputSources = frame.session.getInputSources();
-
-      var newHoveredNodes = [];
-      var lastHoverFrame = this._hoverFrame;
-      this._hoverFrame++;
-
-      var _iteratorNormalCompletion = true;
-      var _didIteratorError = false;
-      var _iteratorError = undefined;
-
-      try {
-        for (var _iterator = inputSources[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-          var inputSource = _step.value;
-
-          var inputPose = frame.getInputPose(inputSource, frameOfRef);
-
-          if (!inputPose) {
-            continue;
-          }
-
-          // Any time that we have a grip matrix, we'll render a controller.
-          if (inputPose.gripMatrix) {
-            this.inputRenderer.addController(inputPose.gripMatrix);
-          }
-
-          if (inputPose.targetRay) {
-            if (inputSource.targetRayMode == 'tracked-pointer') {
-              // If we have a pointer matrix and the pointer origin is the users
-              // hand (as opposed to their head or the screen) use it to render
-              // a ray coming out of the input device to indicate the pointer
-              // direction.
-              this.inputRenderer.addLaserPointer(inputPose.targetRay);
-            }
-
-            // If we have a pointer matrix we can also use it to render a cursor
-            // for both handheld and gaze-based input sources.
-
-            // Check and see if the pointer is pointing at any selectable objects.
-            var hitResult = this.hitTest(inputPose.targetRay);
-
-            if (hitResult) {
-              // Render a cursor at the intersection point.
-              this.inputRenderer.addCursor(hitResult.intersection);
-
-              if (hitResult.node._hoverFrameId != lastHoverFrame) {
-                hitResult.node.onHoverStart();
-              }
-              hitResult.node._hoverFrameId = this._hoverFrame;
-              newHoveredNodes.push(hitResult.node);
-            } else {
-              // Statically render the cursor 1 meters down the ray since we didn't
-              // hit anything selectable.
-              var cursorDistance = 1.0;
-              var cursorPos = _glMatrix.vec3.fromValues(inputPose.targetRay.origin.x, inputPose.targetRay.origin.y, inputPose.targetRay.origin.z);
-              _glMatrix.vec3.add(cursorPos, cursorPos, [inputPose.targetRay.direction.x * cursorDistance, inputPose.targetRay.direction.y * cursorDistance, inputPose.targetRay.direction.z * cursorDistance]);
-              // let cursorPos = vec3.fromValues(0, 0, -1.0);
-              // vec3.transformMat4(cursorPos, cursorPos, inputPose.targetRay);
-              this.inputRenderer.addCursor(cursorPos);
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError = true;
-        _iteratorError = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion && _iterator.return) {
-            _iterator.return();
-          }
-        } finally {
-          if (_didIteratorError) {
-            throw _iteratorError;
-          }
-        }
-      }
-
-      var _iteratorNormalCompletion2 = true;
-      var _didIteratorError2 = false;
-      var _iteratorError2 = undefined;
-
-      try {
-        for (var _iterator2 = this._hoveredNodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-          var hoverNode = _step2.value;
-
-          if (hoverNode._hoverFrameId != this._hoverFrame) {
-            hoverNode.onHoverEnd();
-          }
-        }
-      } catch (err) {
-        _didIteratorError2 = true;
-        _iteratorError2 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion2 && _iterator2.return) {
-            _iterator2.return();
-          }
-        } finally {
-          if (_didIteratorError2) {
-            throw _iteratorError2;
-          }
-        }
-      }
-
-      this._hoveredNodes = newHoveredNodes;
-    }
-  }, {
-    key: 'handleSelect',
-    value: function handleSelect(inputSource, frame, frameOfRef) {
-      var inputPose = frame.getInputPose(inputSource, frameOfRef);
-
-      if (!inputPose) {
-        return;
-      }
-
-      this.handleSelectPointer(inputPose.targetRay);
-    }
-  }, {
-    key: 'handleSelectPointer',
-    value: function handleSelectPointer(targetRay) {
-      if (targetRay) {
-        // Check and see if the pointer is pointing at any selectable objects.
-        var hitResult = this.hitTest(targetRay);
-
-        if (hitResult) {
-          // Render a cursor at the intersection point.
-          hitResult.node.handleSelect();
-        }
-      }
-    }
-  }, {
-    key: 'enableStats',
-    value: function enableStats(enable) {
-      if (enable == this._statsEnabled) {
-        return;
-      }
-
-      this._statsEnabled = enable;
-
-      if (enable) {
-        this._stats = new _statsViewer.StatsViewer();
-        this._stats.selectable = true;
-        this.addNode(this._stats);
-
-        if (this._statsStanding) {
-          this._stats.translation = [0, 1.4, -0.75];
-        } else {
-          this._stats.translation = [0, -0.3, -0.5];
-        }
-        this._stats.scale = [0.3, 0.3, 0.3];
-        _glMatrix.quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);
-      } else if (!enable) {
-        if (this._stats) {
-          this.removeNode(this._stats);
-          this._stats = null;
-        }
-      }
-    }
-  }, {
-    key: 'standingStats',
-    value: function standingStats(enable) {
-      this._statsStanding = enable;
-      if (this._stats) {
-        if (this._statsStanding) {
-          this._stats.translation = [0, 1.4, -0.75];
-        } else {
-          this._stats.translation = [0, -0.3, -0.5];
-        }
-        this._stats.scale = [0.3, 0.3, 0.3];
-        _glMatrix.quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);
-      }
-    }
-  }, {
-    key: 'draw',
-    value: function draw(projectionMatrix, viewMatrix, eye) {
-      var view = new _renderer.RenderView();
-      view.projectionMatrix = projectionMatrix;
-      view.viewMatrix = viewMatrix;
-      if (eye) {
-        view.eye = eye;
-      }
-
-      this.drawViewArray([view]);
-    }
-
-    /** Draws the scene into the base layer of the XRFrame's session */
-
-  }, {
-    key: 'drawXRFrame',
-    value: function drawXRFrame(xrFrame, pose) {
-      if (!this._renderer || !pose) {
-        return;
-      }
-
-      var gl = this._renderer.gl;
-      var session = xrFrame.session;
-      // Assumed to be a XRWebGLLayer for now.
-      var layer = session.baseLayer;
-
-      if (!gl) {
-        return;
-      }
-
-      gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
-
-      if (this.clear) {
-        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-      }
-
-      var views = [];
-      var _iteratorNormalCompletion3 = true;
-      var _didIteratorError3 = false;
-      var _iteratorError3 = undefined;
-
-      try {
-        for (var _iterator3 = pose.views[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
-          var view = _step3.value;
-
-          views.push(new WebXRView(view, layer));
-        }
-      } catch (err) {
-        _didIteratorError3 = true;
-        _iteratorError3 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion3 && _iterator3.return) {
-            _iterator3.return();
-          }
-        } finally {
-          if (_didIteratorError3) {
-            throw _iteratorError3;
-          }
-        }
-      }
-
-      this.drawViewArray(views);
-    }
-  }, {
-    key: 'drawViewArray',
-    value: function drawViewArray(views) {
-      // Don't draw when we don't have a valid context
-      if (!this._renderer) {
-        return;
-      }
-
-      this._renderer.drawViews(views, this);
-    }
-  }, {
-    key: 'startFrame',
-    value: function startFrame() {
-      var prevTimestamp = this._timestamp;
-      this._timestamp = performance.now();
-      if (this._stats) {
-        this._stats.begin();
-      }
-
-      if (prevTimestamp >= 0) {
-        this._frameDelta = this._timestamp - prevTimestamp;
-      } else {
-        this._frameDelta = 0;
-      }
-
-      this._update(this._timestamp, this._frameDelta);
-
-      return this._frameDelta;
-    }
-  }, {
-    key: 'endFrame',
-    value: function endFrame() {
-      if (this._inputRenderer && this._resetInputEndFrame) {
-        this._inputRenderer.reset();
-      }
-
-      if (this._stats) {
-        this._stats.end();
-      }
-    }
-
-    // Override to load scene resources on construction or context restore.
-
-  }, {
-    key: 'onLoadScene',
-    value: function onLoadScene(renderer) {
-      return Promise.resolve();
-    }
-  }, {
-    key: 'inputRenderer',
-    get: function get() {
-      if (!this._inputRenderer) {
-        this._inputRenderer = new _inputRenderer.InputRenderer();
-        this.addNode(this._inputRenderer);
-      }
-      return this._inputRenderer;
-    }
-  }]);
-
-  return Scene;
-}(_node.Node);
-
-/***/ }),
-
-/***/ "./src/util/fallback-helper.js":
-/*!*************************************!*\
-  !*** ./src/util/fallback-helper.js ***!
-  \*************************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.FallbackHelper = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-var _glMatrix = __webpack_require__(/*! ../math/gl-matrix.js */ "./src/math/gl-matrix.js");
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var LOOK_SPEED = 0.0025;
-
-var FallbackHelper = exports.FallbackHelper = function () {
-  function FallbackHelper(scene, gl) {
-    var _this = this;
-
-    _classCallCheck(this, FallbackHelper);
-
-    this.scene = scene;
-    this.gl = gl;
-    this._emulateStage = false;
-
-    this.lookYaw = 0;
-    this.lookPitch = 0;
-
-    this.viewMatrix = _glMatrix.mat4.create();
-
-    var projectionMatrix = _glMatrix.mat4.create();
-    this.projectionMatrix = projectionMatrix;
-
-    // Using a simple identity matrix for the view.
-    _glMatrix.mat4.identity(this.viewMatrix);
-
-    // We need to track the canvas size in order to resize the WebGL
-    // backbuffer width and height, as well as update the projection matrix
-    // and adjust the viewport.
-    function onResize() {
-      gl.canvas.width = gl.canvas.offsetWidth * window.devicePixelRatio;
-      gl.canvas.height = gl.canvas.offsetHeight * window.devicePixelRatio;
-      _glMatrix.mat4.perspective(projectionMatrix, Math.PI * 0.4, gl.canvas.width / gl.canvas.height, 0.1, 1000.0);
-      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
-    }
-    window.addEventListener('resize', onResize);
-    onResize();
-
-    // Upding the view matrix with touch or mouse events.
-    var canvas = gl.canvas;
-    var lastTouchX = 0;
-    var lastTouchY = 0;
-    canvas.addEventListener('touchstart', function (ev) {
-      if (ev.touches.length == 2) {
-        lastTouchX = ev.touches[1].pageX;
-        lastTouchY = ev.touches[1].pageY;
-      }
-    });
-    canvas.addEventListener('touchmove', function (ev) {
-      // Rotate the view when two fingers are being used.
-      if (ev.touches.length == 2) {
-        _this.onLook(ev.touches[1].pageX - lastTouchX, ev.touches[1].pageY - lastTouchY);
-        lastTouchX = ev.touches[1].pageX;
-        lastTouchY = ev.touches[1].pageY;
-      }
-    });
-    canvas.addEventListener('mousemove', function (ev) {
-      // Only rotate when the right button is pressed.
-      if (ev.buttons & 2) {
-        _this.onLook(ev.movementX, ev.movementY);
-      }
-    });
-    canvas.addEventListener('contextmenu', function (ev) {
-      // Prevent context menus on the canvas so that we can use right click to rotate.
-      ev.preventDefault();
-    });
-
-    this.boundOnFrame = this.onFrame.bind(this);
-    window.requestAnimationFrame(this.boundOnFrame);
-  }
-
-  _createClass(FallbackHelper, [{
-    key: 'onLook',
-    value: function onLook(yaw, pitch) {
-      this.lookYaw += yaw * LOOK_SPEED;
-      this.lookPitch += pitch * LOOK_SPEED;
-
-      // Clamp pitch rotation beyond looking straight up or down.
-      if (this.lookPitch < -Math.PI * 0.5) {
-        this.lookPitch = -Math.PI * 0.5;
-      }
-      if (this.lookPitch > Math.PI * 0.5) {
-        this.lookPitch = Math.PI * 0.5;
-      }
-
-      this.updateView();
-    }
-  }, {
-    key: 'onFrame',
-    value: function onFrame(t) {
-      var gl = this.gl;
-      window.requestAnimationFrame(this.boundOnFrame);
-
-      this.scene.startFrame();
-
-      // We can skip setting the framebuffer and viewport every frame, because
-      // it won't change from frame to frame and we're updating the viewport
-      // only when we resize for efficency.
-      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-
-      // We're drawing with our own projection and view matrix now, and we
-      // don't have a list of view to loop through, but otherwise all of the
-      // WebGL drawing logic is exactly the same.
-      this.scene.draw(this.projectionMatrix, this.viewMatrix);
-
-      this.scene.endFrame();
-    }
-  }, {
-    key: 'updateView',
-    value: function updateView() {
-      _glMatrix.mat4.identity(this.viewMatrix);
-
-      _glMatrix.mat4.rotateX(this.viewMatrix, this.viewMatrix, -this.lookPitch);
-      _glMatrix.mat4.rotateY(this.viewMatrix, this.viewMatrix, -this.lookYaw);
-
-      // If we're emulating a stage frame of reference we'll need to move the view
-      // matrix roughly a meter and a half up in the air.
-      if (this._emulateStage) {
-        _glMatrix.mat4.translate(this.viewMatrix, this.viewMatrix, [0, -1.6, 0]);
-      }
-    }
-  }, {
-    key: 'emulateStage',
-    get: function get() {
-      return this._emulateStage;
-    },
-    set: function set(value) {
-      this._emulateStage = value;
-      this.updateView();
-    }
-  }]);
-
-  return FallbackHelper;
-}();
-
-/***/ }),
-
-/***/ "./src/util/query-args.js":
-/*!********************************!*\
-  !*** ./src/util/query-args.js ***!
-  \********************************/
-/*! no static exports found */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-/*
-Provides a simple way to get values from the query string if they're present
-and use a default value if not. Not strictly a "WebGL" utility, but I use it
-frequently enough for debugging that I wanted to include it here.
-
-Example:
-For the URL http://example.com/index.html?particleCount=1000
-
-QueryArgs.getInt("particleCount", 100); // URL overrides, returns 1000
-QueryArgs.getInt("particleSize", 10); // Not in URL, returns default of 10
-*/
-
-var urlArgs = null;
-window.onhashchange = function () {
-  // Force re-parsing on next access
-  urlArgs = null;
-};
-
-function ensureArgsCached() {
-  if (!urlArgs) {
-    urlArgs = {};
-    var query = window.location.search.substring(1) || window.location.hash.substring(1);
-    var vars = query.split('&');
-    for (var i = 0; i < vars.length; i++) {
-      var pair = vars[i].split('=');
-      urlArgs[pair[0].toLowerCase()] = decodeURIComponent(pair[1]);
-    }
-  }
-}
-
-var QueryArgs = exports.QueryArgs = function () {
-  function QueryArgs() {
-    _classCallCheck(this, QueryArgs);
-  }
-
-  _createClass(QueryArgs, null, [{
-    key: 'getString',
-    value: function getString(name, defaultValue) {
-      ensureArgsCached();
-      var lcaseName = name.toLowerCase();
-      if (lcaseName in urlArgs) {
-        return urlArgs[lcaseName];
-      }
-      return defaultValue;
-    }
-  }, {
-    key: 'getInt',
-    value: function getInt(name, defaultValue) {
-      ensureArgsCached();
-      var lcaseName = name.toLowerCase();
-      if (lcaseName in urlArgs) {
-        return parseInt(urlArgs[lcaseName], 10);
-      }
-      return defaultValue;
-    }
-  }, {
-    key: 'getFloat',
-    value: function getFloat(name, defaultValue) {
-      ensureArgsCached();
-      var lcaseName = name.toLowerCase();
-      if (lcaseName in urlArgs) {
-        return parseFloat(urlArgs[lcaseName]);
-      }
-      return defaultValue;
-    }
-  }, {
-    key: 'getBool',
-    value: function getBool(name, defaultValue) {
-      ensureArgsCached();
-      var lcaseName = name.toLowerCase();
-      if (lcaseName in urlArgs) {
-        return parseInt(urlArgs[lcaseName], 10) != 0;
-      }
-      return defaultValue;
-    }
-  }]);
-
-  return QueryArgs;
-}();
-
-/***/ })
-
-/******/ });
-});
-//# sourceMappingURL=cottontail.debug.js.map
\ No newline at end of file
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js.map b/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js.map
deleted file mode 100644
index 1877d6c..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.debug.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap","webpack:///./node_modules/gl-matrix/src/gl-matrix/common.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/mat2.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/mat2d.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/mat3.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/mat4.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/quat.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/quat2.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/vec2.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/vec3.js","webpack:///./node_modules/gl-matrix/src/gl-matrix/vec4.js","webpack:///./src/core/material.js","webpack:///./src/core/node.js","webpack:///./src/core/primitive.js","webpack:///./src/core/program.js","webpack:///./src/core/renderer.js","webpack:///./src/core/texture.js","webpack:///./src/cottontail.js","webpack:///./src/geometry/box-builder.js","webpack:///./src/geometry/primitive-stream.js","webpack:///./src/loaders/gltf2.js","webpack:///./src/materials/pbr.js","webpack:///./src/math/gl-matrix.js","webpack:///./src/math/ray.js","webpack:///./src/nodes/bounds-renderer.js","webpack:///./src/nodes/button.js","webpack:///./src/nodes/cube-sea.js","webpack:///./src/nodes/drop-shadow.js","webpack:///./src/nodes/gltf2.js","webpack:///./src/nodes/input-renderer.js","webpack:///./src/nodes/seven-segment-text.js","webpack:///./src/nodes/skybox.js","webpack:///./src/nodes/stats-viewer.js","webpack:///./src/nodes/video.js","webpack:///./src/scenes/scene.js","webpack:///./src/util/fallback-helper.js","webpack:///./src/util/query-args.js"],"names":["stateToBlendFunc","GL","WebGLRenderingContext","CAP","CULL_FACE","BLEND","DEPTH_TEST","STENCIL_TEST","COLOR_MASK","DEPTH_MASK","STENCIL_MASK","MAT_STATE","CAPS_RANGE","BLEND_SRC_SHIFT","BLEND_SRC_RANGE","BLEND_DST_SHIFT","BLEND_DST_RANGE","BLEND_FUNC_RANGE","DEPTH_FUNC_SHIFT","DEPTH_FUNC_RANGE","RENDER_ORDER","OPAQUE","SKY","TRANSPARENT","ADDITIVE","DEFAULT","state","mask","shift","value","SRC_COLOR","MaterialState","_state","blendFuncSrc","SRC_ALPHA","blendFuncDst","ONE_MINUS_SRC_ALPHA","depthFunc","LESS","NEVER","MaterialSampler","uniformName","_uniformName","_texture","MaterialUniform","defaultValue","length","_value","_length","Array","Material","renderOrder","_samplers","_uniforms","sampler","push","uniform","renderPrimitive","DEFAULT_TRANSLATION","Float32Array","DEFAULT_ROTATION","DEFAULT_SCALE","tmpRayMatrix","mat4","create","Node","name","children","parent","visible","selectable","_matrix","_dirtyTRS","_translation","_rotation","_scale","_dirtyWorldMatrix","_worldMatrix","_activeFrameId","_hoverFrameId","_renderPrimitives","_renderer","_selectHandler","renderer","clearRenderPrimitives","onRendererChanged","child","_setRenderer","cloneNode","vec3","copy","quat","waitForComplete","then","primitive","addRenderPrimitive","addNode","clone","frameId","markActive","removeNode","i","indexOf","splice","setMatrixDirty","fromRotationTranslationScale","childPromises","Promise","all","_instances","index","ray","localRay","_min","invert","worldMatrix","multiply","transformMatrix","Ray","intersection","intersectsAABB","_max","transformMat4","_hitTestSelectableNode","origin","fromValues","x","y","z","node","distance","result","childResult","hitTest","timestamp","frameDelta","onUpdate","_update","_updateLocalMatrix","mul","PrimitiveAttribute","buffer","componentCount","componentType","stride","byteOffset","normalized","Primitive","attributes","elementCount","mode","indexBuffer","indexByteOffset","indexType","min","max","Program","gl","vertSrc","fragSrc","attribMap","defines","_gl","program","createProgram","attrib","_firstUse","_nextUseCallbacks","definesString","define","_vertShader","createShader","VERTEX_SHADER","attachShader","shaderSource","compileShader","_fragShader","FRAGMENT_SHADER","attribName","bindAttribLocation","linkProgram","callback","getProgramParameter","LINK_STATUS","getShaderParameter","COMPILE_STATUS","console","error","getShaderInfoLog","getProgramInfoLog","deleteProgram","attribCount","ACTIVE_ATTRIBUTES","attribInfo","getActiveAttrib","getAttribLocation","uniformCount","ACTIVE_UNIFORMS","uniformInfo","getActiveUniform","replace","getUniformLocation","deleteShader","useProgram","createWebGLContext","ATTRIB","POSITION","NORMAL","TANGENT","TEXCOORD_0","TEXCOORD_1","COLOR_0","ATTRIB_MASK","DEF_LIGHT_DIR","DEF_LIGHT_COLOR","PRECISION_REGEX","RegExp","VERTEX_SHADER_SINGLE_ENTRY","VERTEX_SHADER_MULTI_ENTRY","FRAGMENT_SHADER_ENTRY","isPowerOfTwo","n","glAttribs","alpha","webglCanvas","document","createElement","contextTypes","webgl2","context","contextType","getContext","webglType","RenderView","projectionMatrix","viewMatrix","viewport","eye","_eye","_eyeIndex","RenderBuffer","target","usage","_target","_usage","_buffer","_promise","resolve","RenderPrimitiveAttribute","primitiveAttribute","_attrib_index","_componentCount","_componentType","_stride","_byteOffset","_normalized","RenderPrimitiveAttributeBuffer","_attributes","RenderPrimitive","_material","setPrimitive","_mode","_elementCount","_vao","_complete","_attributeBuffers","_attributeMask","attribute","renderAttribute","foundBuffer","attributeBuffer","_indexBuffer","_indexByteOffset","_indexType","material","reject","completionPromises","_samplerDictionary","_uniform_dictionary","RenderTexture","texture","_activeCallback","inverseMatrix","setCap","glEnum","cap","prevState","change","enable","disable","RenderMaterialSampler","materialSampler","_renderTexture","_getRenderTexture","_index","RenderMaterialUniform","materialUniform","_uniform","RenderMaterial","_program","_completeForActiveFrame","renderSampler","renderUniform","_firstBind","_renderOrder","activeTexture","TEXTURE0","bindTexture","TEXTURE_2D","uniform1fv","uniform2fv","uniform3fv","uniform4fv","otherState","Renderer","_frameId","_programCache","_textureCache","_cameraPositions","_vaoExt","getExtension","fragHighPrecision","getShaderPrecisionFormat","HIGH_FLOAT","_defaultFragPrecision","precision","_depthMaskNeedsReset","_colorMaskNeedsReset","_globalLightColor","_globalLightDir","data","STATIC_DRAW","glBuffer","createBuffer","renderBuffer","bindBuffer","bufferData","byteLength","offset","bufferSubData","updateRenderBuffer","_getMaterialProgram","renderMaterial","setRenderMaterial","meshNode","createRenderPrimitive","views","rootNode","vp","width","height","cameraPosition","set","renderPrimitives","_drawRenderPrimitiveSet","bindVertexArrayOES","depthMask","colorMask","attribMask","use","LIGHT_DIRECTION","LIGHT_COLOR","uniformMatrix4fv","PROJECTION_MATRIX","VIEW_MATRIX","CAMERA_POSITION","uniform1i","EYE_INDEX","eyeIndex","_bindMaterialState","bind","createVertexArrayOES","_bindPrimitive","view","instance","MODEL_MATRIX","drawElements","drawArrays","key","textureKey","Error","textureHandle","createTexture","renderTexture","DataTexture","texImage2D","format","_type","_data","_setSamplerParameters","UNSIGNED_BYTE","source","VideoTexture","_video","addEventListener","paused","waiting","powerOfTwo","mipmap","generateMipmap","minFilter","LINEAR_MIPMAP_LINEAR","LINEAR","wrapS","REPEAT","CLAMP_TO_EDGE","wrapT","texParameteri","TEXTURE_MAG_FILTER","magFilter","TEXTURE_MIN_FILTER","TEXTURE_WRAP_S","TEXTURE_WRAP_T","materialName","vertexSource","fragmentSource","getProgramDefines","_getProgramKey","multiview","fullVertexSource","precisionMatch","match","fragPrecisionHeader","fullFragmentSource","onNextUse","enableVertexAttribArray","disableVertexAttribArray","ARRAY_BUFFER","vertexAttribPointer","ELEMENT_ARRAY_BUFFER","prevMaterial","_capsDiff","colorMaskChange","depthMaskChange","stencilMaskChange","stencilMask","_blendDiff","blendFunc","_depthFuncDiff","TextureSampler","Texture","RGBA","ImageTexture","img","_img","_imgBitmap","src","complete","naturalWidth","_finishImage","window","createImageBitmap","imgBitmap","UrlTexture","url","Image","BlobTexture","blob","URL","createObjectURL","video","readyState","videoWidth","videoHeight","nextDataTextureIndex","type","_width","_height","_format","_key","ColorTexture","r","g","b","a","colorData","Uint8Array","PrimitiveStream","BoxBuilder","PbrMaterial","mat3","vec4","BoundsRenderer","ButtonNode","DropShadowNode","CubeSeaNode","Gltf2Node","SkyboxNode","VideoNode","WebXRView","Scene","FallbackHelper","QueryArgs","stream","primitiveStream","w","h","d","wh","hh","dh","cx","cy","cz","startGeometry","idx","nextVertexIndex","pushTriangle","pushVertex","endGeometry","center","size","hs","pushBox","GeometryBuilderBase","tempVec3","options","_vertices","_indices","_geometryStarted","_vertexOffset","_vertexIndex","_highIndex","_flipWinding","_invertNormals","_transform","_normalTransform","u","v","nx","ny","nz","transformMat3","Math","idxA","idxB","idxC","vertexBuffer","createRenderBuffer","Uint16Array","attribs","FLOAT","setIndexBuffer","setBounds","fromMat4","_stream","finishPrimitive","clear","GLB_MAGIC","CHUNK_TYPE","JSON","BIN","isAbsoluteUri","uri","absRegEx","location","protocol","isDataUri","dataRegEx","resolveUri","baseUrl","getComponentCount","Gltf2Loader","fetch","response","lastIndexOf","substring","endsWith","json","loadFromJson","arrayBuffer","loadFromBinary","headerView","DataView","magic","getUint32","version","chunks","chunkOffset","chunkHeaderView","chunkLength","chunkType","slice","decoder","TextDecoder","jsonString","decode","parse","binaryChunk","asset","minVersion","buffers","Gltf2Resource","bufferViews","bufferView","Gltf2BufferView","images","image","textures","glTexture","getTexture","textureInfo","materials","glMaterial","pbr","pbrMetallicRoughness","baseColorFactor","baseColor","baseColorTexture","metallicRoughnessFactor","metallicFactor","roughnessFactor","metallicRoughness","metallicRoughnessTexture","normal","normalTexture","occlusion","occlusionTexture","occlusionStrength","strength","emissiveFactor","emissive","emissiveTexture","alphaMode","blend","cullFace","doubleSided","accessors","meshes","mesh","glMesh","Gltf2Mesh","primitives","accessor","count","glAttribute","byteStride","glPrimitive","indices","sceneNode","scene","scenes","nodes","nodeId","processNodes","glNode","matrix","translation","rotation","scale","_viewPromise","_renderBuffer","dataView","_dataPromise","base64String","binaryArray","from","atob","c","charCodeAt","Blob","mimeType","VERTEX_SOURCE","EPIC_PBR_FUNCTIONS","FRAGMENT_SOURCE","defineSampler","defineUniform","programDefines","glMatrix","mat2","mat2d","quat2","vec2","normalMat","RAY_INTERSECTION_OFFSET","_dir","dir","bounds","tmin","sign","inv_dir","tmax","tymin","tymax","tzmin","tzmax","t","intersectionPoint","add","normalize","BoundsMaterial","ONE","depthTest","_stageBounds","stageBounds","verts","pointCount","geometry","point","BUTTON_SIZE","BUTTON_CORNER_RADIUS","BUTTON_CORNER_SEGMENTS","BUTTON_ICON_SIZE","BUTTON_LAYER_DISTANCE","BUTTON_COLOR","BUTTON_ALPHA","BUTTON_HOVER_COLOR","BUTTON_HOVER_ALPHA","BUTTON_HOVER_SCALE","BUTTON_HOVER_TRANSITION_TIME_MS","ButtonMaterial","ButtonIconMaterial","icon","iconTexture","_iconTexture","_hovered","_hoverT","hd","ihs","segments","rad","PI","cos","sin","section","floor","buttonPrimitive","_buttonRenderPrimitive","iconPrimitive","iconMaterial","_iconRenderPrimitive","hoverAmount","uniforms","_updateHoverState","samplers","CubeSeaMaterial","heavy","heavyGpu","cubeCount","cubeScale","halfOnly","autoRotate","imageUrl","_renderPrimitive","boxBuilder","pushCube","heroPrimitive","heroNode","createMesh","rebuildCubes","cubeSeaNode","halfGrid","pos","cubeSeaPrimitive","fromRotation","SHADOW_SEGMENTS","SHADOW_GROUND_OFFSET","SHADOW_CENTER_ALPHA","SHADOW_INNER_ALPHA","SHADOW_OUTER_ALPHA","SHADOW_INNER_RADIUS","SHADOW_OUTER_RADIUS","DropShadowMaterial","LEQUAL","segRad","shadowPrimitive","_shadowRenderPrimitive","gltfLoaderMap","WeakMap","_url","_resolver","_rejecter","loader","get","_ensurePromise","loadFromUrl","catch","err","LASER_TEXTURE_DATA","LASER_LENGTH","LASER_DIAMETER","LASER_FADE_END","LASER_FADE_POINT","LASER_DEFAULT_COLOR","CURSOR_RADIUS","CURSOR_SHADOW_RADIUS","CURSOR_SHADOW_INNER_LUMINANCE","CURSOR_SHADOW_OUTER_LUMINANCE","CURSOR_SHADOW_INNER_OPACITY","CURSOR_SHADOW_OUTER_OPACITY","CURSOR_OPACITY","CURSOR_SEGMENTS","CURSOR_DEFAULT_COLOR","CURSOR_DEFAULT_HIDDEN_COLOR","DEFAULT_RESET_OPTIONS","controllers","lasers","cursors","LaserMaterial","laser","laserColor","CURSOR_VERTEX_SHADER","CURSOR_FRAGMENT_SHADER","CursorMaterial","cursorColor","CursorHiddenMaterial","GEQUAL","InputRenderer","_maxInputElements","_controllers","_controllerNode","_controllerNodeHandedness","_lasers","_cursors","_activeControllers","_activeLasers","_activeCursors","controllerNode","handedness","gripMatrix","controller","targetRay","_createLaserMesh","cursorPos","_createCursorMesh","cursor","lr","ll","laserVerts","laserIndices","laserVertexBuffer","laserIndexBuffer","laserIndexCount","laserAttribs","laserPrimitive","laserMaterial","laserRenderPrimitive","cursorVerts","cursorIndices","indexOffset","cursorVertexBuffer","cursorIndexBuffer","cursorIndexCount","cursorAttribs","cursorPrimitive","cursorMaterial","cursorHiddenMaterial","cursorRenderPrimitive","cursorHiddenRenderPrimitive","TEXT_KERNING","SevenSegmentMaterial","SevenSegmentText","_text","_charNodes","clearNodes","vertices","segmentIndices","thickness","defineSegment","id","left","top","right","bottom","characters","defineCharacter","character","segment","vertexAttribs","_charPrimitives","char","charDef","text","charPrimitive","SkyboxMaterial","texCoordScaleOffset","_displayMode","displayMode","_rotationY","rotationY","latSegments","lonSegments","theta","sinTheta","cosTheta","idxOffsetA","idxOffsetB","j","phi","SEGMENTS","MAX_FPS","StatsMaterial","segmentToX","fpsToY","fpsToRGB","now","performance","Date","StatsViewer","_performanceMonitoring","_startTime","_prevFrameTime","_prevGraphUpdateTime","_frames","_fpsAverage","_fpsMin","_fpsStep","_lastSegment","_fpsVertexBuffer","_fpsRenderPrimitive","_fpsNode","_sevenSegmentNode","fpsVerts","fpsIndices","addBGSquare","DYNAMIC_DRAW","fpsIndexBuffer","fpsAttribs","fpsPrimitive","time","frameFps","intervalTime","round","_updateGraph","log","valueLow","valueHigh","color","y0","y1","updateVerts","VideoMaterial","_video_texture","layer","getViewport","_timestamp","_frameDelta","_statsStanding","_stats","_statsEnabled","enableStats","_inputRenderer","_resetInputEndFrame","_lastTimestamp","_hoverFrame","_hoveredNodes","frame","frameOfRef","session","getInputSources","inputSources","newHoveredNodes","lastHoverFrame","inputSource","inputPose","getInputPose","inputRenderer","addController","targetRayMode","addLaserPointer","hitResult","addCursor","onHoverStart","cursorDistance","direction","hoverNode","onHoverEnd","handleSelectPointer","handleSelect","fromEuler","drawViewArray","xrFrame","pose","baseLayer","bindFramebuffer","FRAMEBUFFER","framebuffer","COLOR_BUFFER_BIT","DEPTH_BUFFER_BIT","drawViews","prevTimestamp","begin","reset","end","LOOK_SPEED","_emulateStage","lookYaw","lookPitch","identity","onResize","canvas","offsetWidth","devicePixelRatio","offsetHeight","perspective","drawingBufferWidth","drawingBufferHeight","lastTouchX","lastTouchY","ev","touches","pageX","pageY","onLook","buttons","movementX","movementY","preventDefault","boundOnFrame","onFrame","requestAnimationFrame","yaw","pitch","updateView","startFrame","draw","endFrame","rotateX","rotateY","translate","urlArgs","onhashchange","ensureArgsCached","query","search","hash","vars","split","pair","toLowerCase","decodeURIComponent","lcaseName","parseInt","parseFloat"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;;AClFA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACO;AACA;AACA;;AAEP;AACA;AACA;AACA,WAAW,KAAK;AAChB;AACO;AACP;AACA;;AAEA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;;;;;;;;;;;;ACzCA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAuC;;AAEvC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB;;AAEO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;;;;;;;;;;;;AC9ZP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,MAAM;AACnB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;;;;;;;;;;;;AC/bP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,UAAU,KAAK;AACf,UAAU,KAAK;AACf;AACA,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,UAAU,KAAK;AACf,UAAU,KAAK;AACf;AACA,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAIA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;;;;;;;;;;;;AC1uBP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAGA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA,YAAY,WAAW,WAAW;AAClC;AACA;AACA;AACA;;AAEA,YAAY,WAAW,YAAY;AACnC;AACA;AACA;AACA;;AAEA,aAAa,YAAY,YAAY;AACrC;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH,eAAe,YAAY,YAAY;AACvC,eAAe,YAAY,YAAY;AACvC,eAAe,YAAY,aAAa;;AAExC,iBAAiB,cAAc,cAAc;AAC7C,iBAAiB,cAAc,cAAc;AAC7C,iBAAiB,cAAc,eAAe;;AAE9C;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,YAAY,kDAAgB,GAAG,aAAa;;AAE5C;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA,aAAa,YAAY,YAAY;AACrC,aAAa,YAAY,YAAY;AACrC,aAAa,YAAY,aAAa;;AAEtC;AACA,sBAAsB,yBAAyB;AAC/C,0BAA0B,qBAAqB;AAC/C,0BAA0B,yBAAyB;;AAEnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA,YAAY,kDAAgB,GAAG,aAAa;;AAE5C;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,aAAa,KAAK;AAClB;AACO;AACP,wBAAwB,qDAAmB;AAC3C;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB;AACA,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD,iCAAiC,kDAAgB;AACjD;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;;;;;;;;;;;;AClrDP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAuC;AACN;AACA;AACA;;AAEjC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,OAAO;AACnB;AACO;AACP;AACA;AACA,UAAU,kDAAgB;AAC1B;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uBAAuB,kDAAgB;AACvC;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA,WAAW,iDAAe;AAC1B,WAAW,iDAAe;AAC1B,WAAW,iDAAe;;AAE1B;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACA;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA,oCAAoC;AACpC;AACA,sBAAsB;AACtB;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,EAAE;AACb,WAAW,EAAE;AACb,WAAW,EAAE;AACb,aAAa,KAAK;AAClB;AACA;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACA;AACO,cAAc,8CAAU;;AAE/B;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACA;AACO,mBAAmB,mDAAe;;AAEzC;AACA;AACA;AACA,WAAW,KAAK;AA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A;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP,eAAe,qDAAmB;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP,eAAe,qDAAmB;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA,cAAc,+CAAW;AACzB,EAAE,oDAAgB;AAClB,cAAc,qDAAmB;AACjC,EAAE,uDAAmB;AACrB;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,YAAY,KAAK;AACjB,YAAY,MAAM;AAClB,YAAY,KAAK;AACjB;AACO,gBAAgB,6CAAS;;AAEhC;AACA;AACA,YAAY,KAAK;AACjB,YAAY,MAAM;AAClB,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO,gBAAgB,6CAAS;;AAEhC;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,YAAY,KAAK;AACjB,YAAY,MAAM;AAClB,YAAY,KAAK;AACjB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,gDAAY;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,gDAAY;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE,gDAAY;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA,sBAAsB,kDAAgB;AACtC;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACA;AACO,YAAY,4CAAQ;;AAE3B;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACA;AACO,eAAe,+CAAW;;AAEjC;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACA;AACO,sBAAsB,sDAAkB;;AAE/C;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,MAAM;AACnB;AACA;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,MAAM;AACjB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+BAA+B,kDAAgB;AAC/C,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC,yBAAyB,kDAAgB;AACzC;;;;;;;;;;;;;ACr1BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA,UAAU,iDAAe;AACzB;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,MAAM;AACjB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;;AAGA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,SAAS;AACpB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA;;AAEA,mBAAmB,OAAO;AAC1B,oBAAoB;AACpB;AACA,oBAAoB;AACpB;;AAEA;AACA;AACA,CAAC;;;;;;;;;;;;;AChnBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA,UAAU,iDAAe;AACzB,WAAW,iDAAe;AAC1B;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,SAAS;AACpB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA;;AAEA,mBAAmB,OAAO;AAC1B,oBAAoB,iBAAiB;AACrC;AACA,oBAAoB,iBAAiB;AACrC;;AAEA;AACA;AACA,CAAC;;;;;;;;;;;;;AChwBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAwC;;AAExC;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC,KAAK,qDAAmB;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP,gBAAgB,qDAAmB;AACnC;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,OAAO;AAClB,aAAa,KAAK;AAClB;AACO;AACP;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,iDAAe;AACxB,SAAS,iDAAe;AACxB;AACA,GAAG;AACH;AACA,SAAS,iDAAe;AACxB,SAAS,iDAAe;AACxB;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,KAAK;AAClB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,aAAa,OAAO;AACpB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;;AAEA;AACA;AACA;AACA,WAAW,KAAK;AAChB,WAAW,KAAK;AAChB,aAAa,QAAQ;AACrB;AACO;AACP;AACA;AACA,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C,+BAA+B,kDAAgB;AAC/C;;AAEA;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA,cAAc;AACd;AACA;AACO;;AAEP;AACA;AACA;AACA,WAAW,MAAM;AACjB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,SAAS;AACpB,WAAW,OAAO;AAClB,aAAa,MAAM;AACnB;AACA;AACO;AACP;;AAEA;AACA;AAC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","file":"cottontail.debug.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse {\n\t\tvar a = factory();\n\t\tfor(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n\t}\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/cottontail.js\");\n","/**\n * Common utilities\n * @module glMatrix\n */\n\n// Configuration Constants\nexport const EPSILON = 0.000001;\nexport let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array;\nexport const RANDOM = Math.random;\n\n/**\n * Sets the type of array used when creating new vectors and matrices\n *\n * @param {Type} type Array type, such as Float32Array or Array\n */\nexport function setMatrixArrayType(type) {\n  ARRAY_TYPE = type;\n}\n\nconst degree = Math.PI / 180;\n\n/**\n * Convert Degree To Radian\n *\n * @param {Number} a Angle in Degrees\n */\nexport function toRadian(a) {\n  return a * degree;\n}\n\n/**\n * Tests whether or not the arguments have approximately the same value, within an absolute\n * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less\n * than or equal to 1.0, and a relative tolerance is used for larger values)\n *\n * @param {Number} a The first number to test.\n * @param {Number} b The second number to test.\n * @returns {Boolean} True if the numbers are approximately equal, false otherwise.\n */\nexport function equals(a, b) {\n  return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b));\n}\n","import * as glMatrix from \"./common.js\"\n\n/**\n * 2x2 Matrix\n * @module mat2\n */\n\n/**\n * Creates a new identity mat2\n *\n * @returns {mat2} a new 2x2 matrix\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[1] = 0;\n    out[2] = 0;\n  }\n  out[0] = 1;\n  out[3] = 1;\n  return out;\n}\n\n/**\n * Creates a new mat2 initialized with values from an existing matrix\n *\n * @param {mat2} a matrix to clone\n * @returns {mat2} a new 2x2 matrix\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Copy the values from one mat2 to another\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the source matrix\n * @returns {mat2} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Set a mat2 to the identity matrix\n *\n * @param {mat2} out the receiving matrix\n * @returns {mat2} out\n */\nexport function identity(out) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  return out;\n}\n\n/**\n * Create a new mat2 with the given values\n *\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m10 Component in column 1, row 0 position (index 2)\n * @param {Number} m11 Component in column 1, row 1 position (index 3)\n * @returns {mat2} out A new 2x2 matrix\n */\nexport function fromValues(m00, m01, m10, m11) {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m10;\n  out[3] = m11;\n  return out;\n}\n\n/**\n * Set the components of a mat2 to the given values\n *\n * @param {mat2} out the receiving matrix\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m10 Component in column 1, row 0 position (index 2)\n * @param {Number} m11 Component in column 1, row 1 position (index 3)\n * @returns {mat2} out\n */\nexport function set(out, m00, m01, m10, m11) {\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m10;\n  out[3] = m11;\n  return out;\n}\n\n/**\n * Transpose the values of a mat2\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the source matrix\n * @returns {mat2} out\n */\nexport function transpose(out, a) {\n  // If we are transposing ourselves we can skip a few steps but have to cache\n  // some values\n  if (out === a) {\n    let a1 = a[1];\n    out[1] = a[2];\n    out[2] = a1;\n  } else {\n    out[0] = a[0];\n    out[1] = a[2];\n    out[2] = a[1];\n    out[3] = a[3];\n  }\n\n  return out;\n}\n\n/**\n * Inverts a mat2\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the source matrix\n * @returns {mat2} out\n */\nexport function invert(out, a) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n\n  // Calculate the determinant\n  let det = a0 * a3 - a2 * a1;\n\n  if (!det) {\n    return null;\n  }\n  det = 1.0 / det;\n\n  out[0] =  a3 * det;\n  out[1] = -a1 * det;\n  out[2] = -a2 * det;\n  out[3] =  a0 * det;\n\n  return out;\n}\n\n/**\n * Calculates the adjugate of a mat2\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the source matrix\n * @returns {mat2} out\n */\nexport function adjoint(out, a) {\n  // Caching this value is nessecary if out == a\n  let a0 = a[0];\n  out[0] =  a[3];\n  out[1] = -a[1];\n  out[2] = -a[2];\n  out[3] =  a0;\n\n  return out;\n}\n\n/**\n * Calculates the determinant of a mat2\n *\n * @param {mat2} a the source matrix\n * @returns {Number} determinant of a\n */\nexport function determinant(a) {\n  return a[0] * a[3] - a[2] * a[1];\n}\n\n/**\n * Multiplies two mat2's\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the first operand\n * @param {mat2} b the second operand\n * @returns {mat2} out\n */\nexport function multiply(out, a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n  out[0] = a0 * b0 + a2 * b1;\n  out[1] = a1 * b0 + a3 * b1;\n  out[2] = a0 * b2 + a2 * b3;\n  out[3] = a1 * b2 + a3 * b3;\n  return out;\n}\n\n/**\n * Rotates a mat2 by the given angle\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat2} out\n */\nexport function rotate(out, a, rad) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  out[0] = a0 *  c + a2 * s;\n  out[1] = a1 *  c + a3 * s;\n  out[2] = a0 * -s + a2 * c;\n  out[3] = a1 * -s + a3 * c;\n  return out;\n}\n\n/**\n * Scales the mat2 by the dimensions in the given vec2\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the matrix to rotate\n * @param {vec2} v the vec2 to scale the matrix by\n * @returns {mat2} out\n **/\nexport function scale(out, a, v) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let v0 = v[0], v1 = v[1];\n  out[0] = a0 * v0;\n  out[1] = a1 * v0;\n  out[2] = a2 * v1;\n  out[3] = a3 * v1;\n  return out;\n}\n\n/**\n * Creates a matrix from a given angle\n * This is equivalent to (but much faster than):\n *\n *     mat2.identity(dest);\n *     mat2.rotate(dest, dest, rad);\n *\n * @param {mat2} out mat2 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat2} out\n */\nexport function fromRotation(out, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  out[0] = c;\n  out[1] = s;\n  out[2] = -s;\n  out[3] = c;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector scaling\n * This is equivalent to (but much faster than):\n *\n *     mat2.identity(dest);\n *     mat2.scale(dest, dest, vec);\n *\n * @param {mat2} out mat2 receiving operation result\n * @param {vec2} v Scaling vector\n * @returns {mat2} out\n */\nexport function fromScaling(out, v) {\n  out[0] = v[0];\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = v[1];\n  return out;\n}\n\n/**\n * Returns a string representation of a mat2\n *\n * @param {mat2} a matrix to represent as a string\n * @returns {String} string representation of the matrix\n */\nexport function str(a) {\n  return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';\n}\n\n/**\n * Returns Frobenius norm of a mat2\n *\n * @param {mat2} a the matrix to calculate Frobenius norm of\n * @returns {Number} Frobenius norm\n */\nexport function frob(a) {\n  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2)))\n}\n\n/**\n * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix\n * @param {mat2} L the lower triangular matrix\n * @param {mat2} D the diagonal matrix\n * @param {mat2} U the upper triangular matrix\n * @param {mat2} a the input matrix to factorize\n */\n\nexport function LDU(L, D, U, a) {\n  L[2] = a[2]/a[0];\n  U[0] = a[0];\n  U[1] = a[1];\n  U[3] = a[3] - L[2] * U[1];\n  return [L, D, U];\n}\n\n/**\n * Adds two mat2's\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the first operand\n * @param {mat2} b the second operand\n * @returns {mat2} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  return out;\n}\n\n/**\n * Subtracts matrix b from matrix a\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the first operand\n * @param {mat2} b the second operand\n * @returns {mat2} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  out[3] = a[3] - b[3];\n  return out;\n}\n\n/**\n * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)\n *\n * @param {mat2} a The first matrix.\n * @param {mat2} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];\n}\n\n/**\n * Returns whether or not the matrices have approximately the same elements in the same position.\n *\n * @param {mat2} a The first matrix.\n * @param {mat2} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));\n}\n\n/**\n * Multiply each element of the matrix by a scalar.\n *\n * @param {mat2} out the receiving matrix\n * @param {mat2} a the matrix to scale\n * @param {Number} b amount to scale the matrix's elements by\n * @returns {mat2} out\n */\nexport function multiplyScalar(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  return out;\n}\n\n/**\n * Adds two mat2's after multiplying each element of the second operand by a scalar value.\n *\n * @param {mat2} out the receiving vector\n * @param {mat2} a the first operand\n * @param {mat2} b the second operand\n * @param {Number} scale the amount to scale b's elements by before adding\n * @returns {mat2} out\n */\nexport function multiplyScalarAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  out[3] = a[3] + (b[3] * scale);\n  return out;\n}\n\n/**\n * Alias for {@link mat2.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link mat2.subtract}\n * @function\n */\nexport const sub = subtract;\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 2x3 Matrix\n * @module mat2d\n *\n * @description\n * A mat2d contains six elements defined as:\n * <pre>\n * [a, c, tx,\n *  b, d, ty]\n * </pre>\n * This is a short form for the 3x3 matrix:\n * <pre>\n * [a, c, tx,\n *  b, d, ty,\n *  0, 0, 1]\n * </pre>\n * The last row is ignored so the array is shorter and operations are faster.\n */\n\n/**\n * Creates a new identity mat2d\n *\n * @returns {mat2d} a new 2x3 matrix\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(6);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[1] = 0;\n    out[2] = 0;\n    out[4] = 0;\n    out[5] = 0;\n  }\n  out[0] = 1;\n  out[3] = 1;\n  return out;\n}\n\n/**\n * Creates a new mat2d initialized with values from an existing matrix\n *\n * @param {mat2d} a matrix to clone\n * @returns {mat2d} a new 2x3 matrix\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(6);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  return out;\n}\n\n/**\n * Copy the values from one mat2d to another\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the source matrix\n * @returns {mat2d} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  return out;\n}\n\n/**\n * Set a mat2d to the identity matrix\n *\n * @param {mat2d} out the receiving matrix\n * @returns {mat2d} out\n */\nexport function identity(out) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  out[4] = 0;\n  out[5] = 0;\n  return out;\n}\n\n/**\n * Create a new mat2d with the given values\n *\n * @param {Number} a Component A (index 0)\n * @param {Number} b Component B (index 1)\n * @param {Number} c Component C (index 2)\n * @param {Number} d Component D (index 3)\n * @param {Number} tx Component TX (index 4)\n * @param {Number} ty Component TY (index 5)\n * @returns {mat2d} A new mat2d\n */\nexport function fromValues(a, b, c, d, tx, ty) {\n  let out = new glMatrix.ARRAY_TYPE(6);\n  out[0] = a;\n  out[1] = b;\n  out[2] = c;\n  out[3] = d;\n  out[4] = tx;\n  out[5] = ty;\n  return out;\n}\n\n/**\n * Set the components of a mat2d to the given values\n *\n * @param {mat2d} out the receiving matrix\n * @param {Number} a Component A (index 0)\n * @param {Number} b Component B (index 1)\n * @param {Number} c Component C (index 2)\n * @param {Number} d Component D (index 3)\n * @param {Number} tx Component TX (index 4)\n * @param {Number} ty Component TY (index 5)\n * @returns {mat2d} out\n */\nexport function set(out, a, b, c, d, tx, ty) {\n  out[0] = a;\n  out[1] = b;\n  out[2] = c;\n  out[3] = d;\n  out[4] = tx;\n  out[5] = ty;\n  return out;\n}\n\n/**\n * Inverts a mat2d\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the source matrix\n * @returns {mat2d} out\n */\nexport function invert(out, a) {\n  let aa = a[0], ab = a[1], ac = a[2], ad = a[3];\n  let atx = a[4], aty = a[5];\n\n  let det = aa * ad - ab * ac;\n  if(!det){\n    return null;\n  }\n  det = 1.0 / det;\n\n  out[0] = ad * det;\n  out[1] = -ab * det;\n  out[2] = -ac * det;\n  out[3] = aa * det;\n  out[4] = (ac * aty - ad * atx) * det;\n  out[5] = (ab * atx - aa * aty) * det;\n  return out;\n}\n\n/**\n * Calculates the determinant of a mat2d\n *\n * @param {mat2d} a the source matrix\n * @returns {Number} determinant of a\n */\nexport function determinant(a) {\n  return a[0] * a[3] - a[1] * a[2];\n}\n\n/**\n * Multiplies two mat2d's\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the first operand\n * @param {mat2d} b the second operand\n * @returns {mat2d} out\n */\nexport function multiply(out, a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];\n  out[0] = a0 * b0 + a2 * b1;\n  out[1] = a1 * b0 + a3 * b1;\n  out[2] = a0 * b2 + a2 * b3;\n  out[3] = a1 * b2 + a3 * b3;\n  out[4] = a0 * b4 + a2 * b5 + a4;\n  out[5] = a1 * b4 + a3 * b5 + a5;\n  return out;\n}\n\n/**\n * Rotates a mat2d by the given angle\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat2d} out\n */\nexport function rotate(out, a, rad) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  out[0] = a0 *  c + a2 * s;\n  out[1] = a1 *  c + a3 * s;\n  out[2] = a0 * -s + a2 * c;\n  out[3] = a1 * -s + a3 * c;\n  out[4] = a4;\n  out[5] = a5;\n  return out;\n}\n\n/**\n * Scales the mat2d by the dimensions in the given vec2\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the matrix to translate\n * @param {vec2} v the vec2 to scale the matrix by\n * @returns {mat2d} out\n **/\nexport function scale(out, a, v) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];\n  let v0 = v[0], v1 = v[1];\n  out[0] = a0 * v0;\n  out[1] = a1 * v0;\n  out[2] = a2 * v1;\n  out[3] = a3 * v1;\n  out[4] = a4;\n  out[5] = a5;\n  return out;\n}\n\n/**\n * Translates the mat2d by the dimensions in the given vec2\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the matrix to translate\n * @param {vec2} v the vec2 to translate the matrix by\n * @returns {mat2d} out\n **/\nexport function translate(out, a, v) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];\n  let v0 = v[0], v1 = v[1];\n  out[0] = a0;\n  out[1] = a1;\n  out[2] = a2;\n  out[3] = a3;\n  out[4] = a0 * v0 + a2 * v1 + a4;\n  out[5] = a1 * v0 + a3 * v1 + a5;\n  return out;\n}\n\n/**\n * Creates a matrix from a given angle\n * This is equivalent to (but much faster than):\n *\n *     mat2d.identity(dest);\n *     mat2d.rotate(dest, dest, rad);\n *\n * @param {mat2d} out mat2d receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat2d} out\n */\nexport function fromRotation(out, rad) {\n  let s = Math.sin(rad), c = Math.cos(rad);\n  out[0] = c;\n  out[1] = s;\n  out[2] = -s;\n  out[3] = c;\n  out[4] = 0;\n  out[5] = 0;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector scaling\n * This is equivalent to (but much faster than):\n *\n *     mat2d.identity(dest);\n *     mat2d.scale(dest, dest, vec);\n *\n * @param {mat2d} out mat2d receiving operation result\n * @param {vec2} v Scaling vector\n * @returns {mat2d} out\n */\nexport function fromScaling(out, v) {\n  out[0] = v[0];\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = v[1];\n  out[4] = 0;\n  out[5] = 0;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector translation\n * This is equivalent to (but much faster than):\n *\n *     mat2d.identity(dest);\n *     mat2d.translate(dest, dest, vec);\n *\n * @param {mat2d} out mat2d receiving operation result\n * @param {vec2} v Translation vector\n * @returns {mat2d} out\n */\nexport function fromTranslation(out, v) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  out[4] = v[0];\n  out[5] = v[1];\n  return out;\n}\n\n/**\n * Returns a string representation of a mat2d\n *\n * @param {mat2d} a matrix to represent as a string\n * @returns {String} string representation of the matrix\n */\nexport function str(a) {\n  return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +\n          a[3] + ', ' + a[4] + ', ' + a[5] + ')';\n}\n\n/**\n * Returns Frobenius norm of a mat2d\n *\n * @param {mat2d} a the matrix to calculate Frobenius norm of\n * @returns {Number} Frobenius norm\n */\nexport function frob(a) {\n  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1))\n}\n\n/**\n * Adds two mat2d's\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the first operand\n * @param {mat2d} b the second operand\n * @returns {mat2d} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  out[4] = a[4] + b[4];\n  out[5] = a[5] + b[5];\n  return out;\n}\n\n/**\n * Subtracts matrix b from matrix a\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the first operand\n * @param {mat2d} b the second operand\n * @returns {mat2d} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  out[3] = a[3] - b[3];\n  out[4] = a[4] - b[4];\n  out[5] = a[5] - b[5];\n  return out;\n}\n\n/**\n * Multiply each element of the matrix by a scalar.\n *\n * @param {mat2d} out the receiving matrix\n * @param {mat2d} a the matrix to scale\n * @param {Number} b amount to scale the matrix's elements by\n * @returns {mat2d} out\n */\nexport function multiplyScalar(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  out[4] = a[4] * b;\n  out[5] = a[5] * b;\n  return out;\n}\n\n/**\n * Adds two mat2d's after multiplying each element of the second operand by a scalar value.\n *\n * @param {mat2d} out the receiving vector\n * @param {mat2d} a the first operand\n * @param {mat2d} b the second operand\n * @param {Number} scale the amount to scale b's elements by before adding\n * @returns {mat2d} out\n */\nexport function multiplyScalarAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  out[3] = a[3] + (b[3] * scale);\n  out[4] = a[4] + (b[4] * scale);\n  out[5] = a[5] + (b[5] * scale);\n  return out;\n}\n\n/**\n * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)\n *\n * @param {mat2d} a The first matrix.\n * @param {mat2d} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];\n}\n\n/**\n * Returns whether or not the matrices have approximately the same elements in the same position.\n *\n * @param {mat2d} a The first matrix.\n * @param {mat2d} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&\n          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&\n          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)));\n}\n\n/**\n * Alias for {@link mat2d.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link mat2d.subtract}\n * @function\n */\nexport const sub = subtract;\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 3x3 Matrix\n * @module mat3\n */\n\n/**\n * Creates a new identity mat3\n *\n * @returns {mat3} a new 3x3 matrix\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(9);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[1] = 0;\n    out[2] = 0;\n    out[3] = 0;\n    out[5] = 0;\n    out[6] = 0;\n    out[7] = 0;\n  }\n  out[0] = 1;\n  out[4] = 1;\n  out[8] = 1;\n  return out;\n}\n\n/**\n * Copies the upper-left 3x3 values into the given mat3.\n *\n * @param {mat3} out the receiving 3x3 matrix\n * @param {mat4} a   the source 4x4 matrix\n * @returns {mat3} out\n */\nexport function fromMat4(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[4];\n  out[4] = a[5];\n  out[5] = a[6];\n  out[6] = a[8];\n  out[7] = a[9];\n  out[8] = a[10];\n  return out;\n}\n\n/**\n * Creates a new mat3 initialized with values from an existing matrix\n *\n * @param {mat3} a matrix to clone\n * @returns {mat3} a new 3x3 matrix\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(9);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  out[6] = a[6];\n  out[7] = a[7];\n  out[8] = a[8];\n  return out;\n}\n\n/**\n * Copy the values from one mat3 to another\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the source matrix\n * @returns {mat3} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  out[6] = a[6];\n  out[7] = a[7];\n  out[8] = a[8];\n  return out;\n}\n\n/**\n * Create a new mat3 with the given values\n *\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m02 Component in column 0, row 2 position (index 2)\n * @param {Number} m10 Component in column 1, row 0 position (index 3)\n * @param {Number} m11 Component in column 1, row 1 position (index 4)\n * @param {Number} m12 Component in column 1, row 2 position (index 5)\n * @param {Number} m20 Component in column 2, row 0 position (index 6)\n * @param {Number} m21 Component in column 2, row 1 position (index 7)\n * @param {Number} m22 Component in column 2, row 2 position (index 8)\n * @returns {mat3} A new mat3\n */\nexport function fromValues(m00, m01, m02, m10, m11, m12, m20, m21, m22) {\n  let out = new glMatrix.ARRAY_TYPE(9);\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m02;\n  out[3] = m10;\n  out[4] = m11;\n  out[5] = m12;\n  out[6] = m20;\n  out[7] = m21;\n  out[8] = m22;\n  return out;\n}\n\n/**\n * Set the components of a mat3 to the given values\n *\n * @param {mat3} out the receiving matrix\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m02 Component in column 0, row 2 position (index 2)\n * @param {Number} m10 Component in column 1, row 0 position (index 3)\n * @param {Number} m11 Component in column 1, row 1 position (index 4)\n * @param {Number} m12 Component in column 1, row 2 position (index 5)\n * @param {Number} m20 Component in column 2, row 0 position (index 6)\n * @param {Number} m21 Component in column 2, row 1 position (index 7)\n * @param {Number} m22 Component in column 2, row 2 position (index 8)\n * @returns {mat3} out\n */\nexport function set(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m02;\n  out[3] = m10;\n  out[4] = m11;\n  out[5] = m12;\n  out[6] = m20;\n  out[7] = m21;\n  out[8] = m22;\n  return out;\n}\n\n/**\n * Set a mat3 to the identity matrix\n *\n * @param {mat3} out the receiving matrix\n * @returns {mat3} out\n */\nexport function identity(out) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 1;\n  out[5] = 0;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 1;\n  return out;\n}\n\n/**\n * Transpose the values of a mat3\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the source matrix\n * @returns {mat3} out\n */\nexport function transpose(out, a) {\n  // If we are transposing ourselves we can skip a few steps but have to cache some values\n  if (out === a) {\n    let a01 = a[1], a02 = a[2], a12 = a[5];\n    out[1] = a[3];\n    out[2] = a[6];\n    out[3] = a01;\n    out[5] = a[7];\n    out[6] = a02;\n    out[7] = a12;\n  } else {\n    out[0] = a[0];\n    out[1] = a[3];\n    out[2] = a[6];\n    out[3] = a[1];\n    out[4] = a[4];\n    out[5] = a[7];\n    out[6] = a[2];\n    out[7] = a[5];\n    out[8] = a[8];\n  }\n\n  return out;\n}\n\n/**\n * Inverts a mat3\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the source matrix\n * @returns {mat3} out\n */\nexport function invert(out, a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2];\n  let a10 = a[3], a11 = a[4], a12 = a[5];\n  let a20 = a[6], a21 = a[7], a22 = a[8];\n\n  let b01 = a22 * a11 - a12 * a21;\n  let b11 = -a22 * a10 + a12 * a20;\n  let b21 = a21 * a10 - a11 * a20;\n\n  // Calculate the determinant\n  let det = a00 * b01 + a01 * b11 + a02 * b21;\n\n  if (!det) {\n    return null;\n  }\n  det = 1.0 / det;\n\n  out[0] = b01 * det;\n  out[1] = (-a22 * a01 + a02 * a21) * det;\n  out[2] = (a12 * a01 - a02 * a11) * det;\n  out[3] = b11 * det;\n  out[4] = (a22 * a00 - a02 * a20) * det;\n  out[5] = (-a12 * a00 + a02 * a10) * det;\n  out[6] = b21 * det;\n  out[7] = (-a21 * a00 + a01 * a20) * det;\n  out[8] = (a11 * a00 - a01 * a10) * det;\n  return out;\n}\n\n/**\n * Calculates the adjugate of a mat3\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the source matrix\n * @returns {mat3} out\n */\nexport function adjoint(out, a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2];\n  let a10 = a[3], a11 = a[4], a12 = a[5];\n  let a20 = a[6], a21 = a[7], a22 = a[8];\n\n  out[0] = (a11 * a22 - a12 * a21);\n  out[1] = (a02 * a21 - a01 * a22);\n  out[2] = (a01 * a12 - a02 * a11);\n  out[3] = (a12 * a20 - a10 * a22);\n  out[4] = (a00 * a22 - a02 * a20);\n  out[5] = (a02 * a10 - a00 * a12);\n  out[6] = (a10 * a21 - a11 * a20);\n  out[7] = (a01 * a20 - a00 * a21);\n  out[8] = (a00 * a11 - a01 * a10);\n  return out;\n}\n\n/**\n * Calculates the determinant of a mat3\n *\n * @param {mat3} a the source matrix\n * @returns {Number} determinant of a\n */\nexport function determinant(a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2];\n  let a10 = a[3], a11 = a[4], a12 = a[5];\n  let a20 = a[6], a21 = a[7], a22 = a[8];\n\n  return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);\n}\n\n/**\n * Multiplies two mat3's\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the first operand\n * @param {mat3} b the second operand\n * @returns {mat3} out\n */\nexport function multiply(out, a, b) {\n  let a00 = a[0], a01 = a[1], a02 = a[2];\n  let a10 = a[3], a11 = a[4], a12 = a[5];\n  let a20 = a[6], a21 = a[7], a22 = a[8];\n\n  let b00 = b[0], b01 = b[1], b02 = b[2];\n  let b10 = b[3], b11 = b[4], b12 = b[5];\n  let b20 = b[6], b21 = b[7], b22 = b[8];\n\n  out[0] = b00 * a00 + b01 * a10 + b02 * a20;\n  out[1] = b00 * a01 + b01 * a11 + b02 * a21;\n  out[2] = b00 * a02 + b01 * a12 + b02 * a22;\n\n  out[3] = b10 * a00 + b11 * a10 + b12 * a20;\n  out[4] = b10 * a01 + b11 * a11 + b12 * a21;\n  out[5] = b10 * a02 + b11 * a12 + b12 * a22;\n\n  out[6] = b20 * a00 + b21 * a10 + b22 * a20;\n  out[7] = b20 * a01 + b21 * a11 + b22 * a21;\n  out[8] = b20 * a02 + b21 * a12 + b22 * a22;\n  return out;\n}\n\n/**\n * Translate a mat3 by the given vector\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the matrix to translate\n * @param {vec2} v vector to translate by\n * @returns {mat3} out\n */\nexport function translate(out, a, v) {\n  let a00 = a[0], a01 = a[1], a02 = a[2],\n    a10 = a[3], a11 = a[4], a12 = a[5],\n    a20 = a[6], a21 = a[7], a22 = a[8],\n    x = v[0], y = v[1];\n\n  out[0] = a00;\n  out[1] = a01;\n  out[2] = a02;\n\n  out[3] = a10;\n  out[4] = a11;\n  out[5] = a12;\n\n  out[6] = x * a00 + y * a10 + a20;\n  out[7] = x * a01 + y * a11 + a21;\n  out[8] = x * a02 + y * a12 + a22;\n  return out;\n}\n\n/**\n * Rotates a mat3 by the given angle\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat3} out\n */\nexport function rotate(out, a, rad) {\n  let a00 = a[0], a01 = a[1], a02 = a[2],\n    a10 = a[3], a11 = a[4], a12 = a[5],\n    a20 = a[6], a21 = a[7], a22 = a[8],\n\n    s = Math.sin(rad),\n    c = Math.cos(rad);\n\n  out[0] = c * a00 + s * a10;\n  out[1] = c * a01 + s * a11;\n  out[2] = c * a02 + s * a12;\n\n  out[3] = c * a10 - s * a00;\n  out[4] = c * a11 - s * a01;\n  out[5] = c * a12 - s * a02;\n\n  out[6] = a20;\n  out[7] = a21;\n  out[8] = a22;\n  return out;\n};\n\n/**\n * Scales the mat3 by the dimensions in the given vec2\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the matrix to rotate\n * @param {vec2} v the vec2 to scale the matrix by\n * @returns {mat3} out\n **/\nexport function scale(out, a, v) {\n  let x = v[0], y = v[1];\n\n  out[0] = x * a[0];\n  out[1] = x * a[1];\n  out[2] = x * a[2];\n\n  out[3] = y * a[3];\n  out[4] = y * a[4];\n  out[5] = y * a[5];\n\n  out[6] = a[6];\n  out[7] = a[7];\n  out[8] = a[8];\n  return out;\n}\n\n/**\n * Creates a matrix from a vector translation\n * This is equivalent to (but much faster than):\n *\n *     mat3.identity(dest);\n *     mat3.translate(dest, dest, vec);\n *\n * @param {mat3} out mat3 receiving operation result\n * @param {vec2} v Translation vector\n * @returns {mat3} out\n */\nexport function fromTranslation(out, v) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 1;\n  out[5] = 0;\n  out[6] = v[0];\n  out[7] = v[1];\n  out[8] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from a given angle\n * This is equivalent to (but much faster than):\n *\n *     mat3.identity(dest);\n *     mat3.rotate(dest, dest, rad);\n *\n * @param {mat3} out mat3 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat3} out\n */\nexport function fromRotation(out, rad) {\n  let s = Math.sin(rad), c = Math.cos(rad);\n\n  out[0] = c;\n  out[1] = s;\n  out[2] = 0;\n\n  out[3] = -s;\n  out[4] = c;\n  out[5] = 0;\n\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector scaling\n * This is equivalent to (but much faster than):\n *\n *     mat3.identity(dest);\n *     mat3.scale(dest, dest, vec);\n *\n * @param {mat3} out mat3 receiving operation result\n * @param {vec2} v Scaling vector\n * @returns {mat3} out\n */\nexport function fromScaling(out, v) {\n  out[0] = v[0];\n  out[1] = 0;\n  out[2] = 0;\n\n  out[3] = 0;\n  out[4] = v[1];\n  out[5] = 0;\n\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 1;\n  return out;\n}\n\n/**\n * Copies the values from a mat2d into a mat3\n *\n * @param {mat3} out the receiving matrix\n * @param {mat2d} a the matrix to copy\n * @returns {mat3} out\n **/\nexport function fromMat2d(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = 0;\n\n  out[3] = a[2];\n  out[4] = a[3];\n  out[5] = 0;\n\n  out[6] = a[4];\n  out[7] = a[5];\n  out[8] = 1;\n  return out;\n}\n\n/**\n* Calculates a 3x3 matrix from the given quaternion\n*\n* @param {mat3} out mat3 receiving operation result\n* @param {quat} q Quaternion to create matrix from\n*\n* @returns {mat3} out\n*/\nexport function fromQuat(out, q) {\n  let x = q[0], y = q[1], z = q[2], w = q[3];\n  let x2 = x + x;\n  let y2 = y + y;\n  let z2 = z + z;\n\n  let xx = x * x2;\n  let yx = y * x2;\n  let yy = y * y2;\n  let zx = z * x2;\n  let zy = z * y2;\n  let zz = z * z2;\n  let wx = w * x2;\n  let wy = w * y2;\n  let wz = w * z2;\n\n  out[0] = 1 - yy - zz;\n  out[3] = yx - wz;\n  out[6] = zx + wy;\n\n  out[1] = yx + wz;\n  out[4] = 1 - xx - zz;\n  out[7] = zy - wx;\n\n  out[2] = zx - wy;\n  out[5] = zy + wx;\n  out[8] = 1 - xx - yy;\n\n  return out;\n}\n\n/**\n* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix\n*\n* @param {mat3} out mat3 receiving operation result\n* @param {mat4} a Mat4 to derive the normal matrix from\n*\n* @returns {mat3} out\n*/\nexport function normalFromMat4(out, a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n\n  let b00 = a00 * a11 - a01 * a10;\n  let b01 = a00 * a12 - a02 * a10;\n  let b02 = a00 * a13 - a03 * a10;\n  let b03 = a01 * a12 - a02 * a11;\n  let b04 = a01 * a13 - a03 * a11;\n  let b05 = a02 * a13 - a03 * a12;\n  let b06 = a20 * a31 - a21 * a30;\n  let b07 = a20 * a32 - a22 * a30;\n  let b08 = a20 * a33 - a23 * a30;\n  let b09 = a21 * a32 - a22 * a31;\n  let b10 = a21 * a33 - a23 * a31;\n  let b11 = a22 * a33 - a23 * a32;\n\n  // Calculate the determinant\n  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n\n  if (!det) {\n    return null;\n  }\n  det = 1.0 / det;\n\n  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;\n  out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;\n  out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;\n\n  out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;\n  out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;\n  out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;\n\n  out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;\n  out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;\n  out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;\n\n  return out;\n}\n\n/**\n * Generates a 2D projection matrix with the given bounds\n *\n * @param {mat3} out mat3 frustum matrix will be written into\n * @param {number} width Width of your gl context\n * @param {number} height Height of gl context\n * @returns {mat3} out\n */\nexport function projection(out, width, height) {\n    out[0] = 2 / width;\n    out[1] = 0;\n    out[2] = 0;\n    out[3] = 0;\n    out[4] = -2 / height;\n    out[5] = 0;\n    out[6] = -1;\n    out[7] = 1;\n    out[8] = 1;\n    return out;\n}\n\n/**\n * Returns a string representation of a mat3\n *\n * @param {mat3} a matrix to represent as a string\n * @returns {String} string representation of the matrix\n */\nexport function str(a) {\n  return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +\n          a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +\n          a[6] + ', ' + a[7] + ', ' + a[8] + ')';\n}\n\n/**\n * Returns Frobenius norm of a mat3\n *\n * @param {mat3} a the matrix to calculate Frobenius norm of\n * @returns {Number} Frobenius norm\n */\nexport function frob(a) {\n  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2)))\n}\n\n/**\n * Adds two mat3's\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the first operand\n * @param {mat3} b the second operand\n * @returns {mat3} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  out[4] = a[4] + b[4];\n  out[5] = a[5] + b[5];\n  out[6] = a[6] + b[6];\n  out[7] = a[7] + b[7];\n  out[8] = a[8] + b[8];\n  return out;\n}\n\n/**\n * Subtracts matrix b from matrix a\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the first operand\n * @param {mat3} b the second operand\n * @returns {mat3} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  out[3] = a[3] - b[3];\n  out[4] = a[4] - b[4];\n  out[5] = a[5] - b[5];\n  out[6] = a[6] - b[6];\n  out[7] = a[7] - b[7];\n  out[8] = a[8] - b[8];\n  return out;\n}\n\n\n\n/**\n * Multiply each element of the matrix by a scalar.\n *\n * @param {mat3} out the receiving matrix\n * @param {mat3} a the matrix to scale\n * @param {Number} b amount to scale the matrix's elements by\n * @returns {mat3} out\n */\nexport function multiplyScalar(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  out[4] = a[4] * b;\n  out[5] = a[5] * b;\n  out[6] = a[6] * b;\n  out[7] = a[7] * b;\n  out[8] = a[8] * b;\n  return out;\n}\n\n/**\n * Adds two mat3's after multiplying each element of the second operand by a scalar value.\n *\n * @param {mat3} out the receiving vector\n * @param {mat3} a the first operand\n * @param {mat3} b the second operand\n * @param {Number} scale the amount to scale b's elements by before adding\n * @returns {mat3} out\n */\nexport function multiplyScalarAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  out[3] = a[3] + (b[3] * scale);\n  out[4] = a[4] + (b[4] * scale);\n  out[5] = a[5] + (b[5] * scale);\n  out[6] = a[6] + (b[6] * scale);\n  out[7] = a[7] + (b[7] * scale);\n  out[8] = a[8] + (b[8] * scale);\n  return out;\n}\n\n/**\n * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)\n *\n * @param {mat3} a The first matrix.\n * @param {mat3} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] &&\n         a[3] === b[3] && a[4] === b[4] && a[5] === b[5] &&\n         a[6] === b[6] && a[7] === b[7] && a[8] === b[8];\n}\n\n/**\n * Returns whether or not the matrices have approximately the same elements in the same position.\n *\n * @param {mat3} a The first matrix.\n * @param {mat3} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7], a8 = a[8];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&\n          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&\n          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&\n          Math.abs(a6 - b6) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&\n          Math.abs(a7 - b7) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&\n          Math.abs(a8 - b8) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a8), Math.abs(b8)));\n}\n\n/**\n * Alias for {@link mat3.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link mat3.subtract}\n * @function\n */\nexport const sub = subtract;\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.\n * @module mat4\n */\n\n/**\n * Creates a new identity mat4\n *\n * @returns {mat4} a new 4x4 matrix\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(16);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[1] = 0;\n    out[2] = 0;\n    out[3] = 0;\n    out[4] = 0;\n    out[6] = 0;\n    out[7] = 0;\n    out[8] = 0;\n    out[9] = 0;\n    out[11] = 0;\n    out[12] = 0;\n    out[13] = 0;\n    out[14] = 0;\n  }\n  out[0] = 1;\n  out[5] = 1;\n  out[10] = 1;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a new mat4 initialized with values from an existing matrix\n *\n * @param {mat4} a matrix to clone\n * @returns {mat4} a new 4x4 matrix\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(16);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  out[6] = a[6];\n  out[7] = a[7];\n  out[8] = a[8];\n  out[9] = a[9];\n  out[10] = a[10];\n  out[11] = a[11];\n  out[12] = a[12];\n  out[13] = a[13];\n  out[14] = a[14];\n  out[15] = a[15];\n  return out;\n}\n\n/**\n * Copy the values from one mat4 to another\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the source matrix\n * @returns {mat4} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  out[6] = a[6];\n  out[7] = a[7];\n  out[8] = a[8];\n  out[9] = a[9];\n  out[10] = a[10];\n  out[11] = a[11];\n  out[12] = a[12];\n  out[13] = a[13];\n  out[14] = a[14];\n  out[15] = a[15];\n  return out;\n}\n\n/**\n * Create a new mat4 with the given values\n *\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m02 Component in column 0, row 2 position (index 2)\n * @param {Number} m03 Component in column 0, row 3 position (index 3)\n * @param {Number} m10 Component in column 1, row 0 position (index 4)\n * @param {Number} m11 Component in column 1, row 1 position (index 5)\n * @param {Number} m12 Component in column 1, row 2 position (index 6)\n * @param {Number} m13 Component in column 1, row 3 position (index 7)\n * @param {Number} m20 Component in column 2, row 0 position (index 8)\n * @param {Number} m21 Component in column 2, row 1 position (index 9)\n * @param {Number} m22 Component in column 2, row 2 position (index 10)\n * @param {Number} m23 Component in column 2, row 3 position (index 11)\n * @param {Number} m30 Component in column 3, row 0 position (index 12)\n * @param {Number} m31 Component in column 3, row 1 position (index 13)\n * @param {Number} m32 Component in column 3, row 2 position (index 14)\n * @param {Number} m33 Component in column 3, row 3 position (index 15)\n * @returns {mat4} A new mat4\n */\nexport function fromValues(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {\n  let out = new glMatrix.ARRAY_TYPE(16);\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m02;\n  out[3] = m03;\n  out[4] = m10;\n  out[5] = m11;\n  out[6] = m12;\n  out[7] = m13;\n  out[8] = m20;\n  out[9] = m21;\n  out[10] = m22;\n  out[11] = m23;\n  out[12] = m30;\n  out[13] = m31;\n  out[14] = m32;\n  out[15] = m33;\n  return out;\n}\n\n/**\n * Set the components of a mat4 to the given values\n *\n * @param {mat4} out the receiving matrix\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m02 Component in column 0, row 2 position (index 2)\n * @param {Number} m03 Component in column 0, row 3 position (index 3)\n * @param {Number} m10 Component in column 1, row 0 position (index 4)\n * @param {Number} m11 Component in column 1, row 1 position (index 5)\n * @param {Number} m12 Component in column 1, row 2 position (index 6)\n * @param {Number} m13 Component in column 1, row 3 position (index 7)\n * @param {Number} m20 Component in column 2, row 0 position (index 8)\n * @param {Number} m21 Component in column 2, row 1 position (index 9)\n * @param {Number} m22 Component in column 2, row 2 position (index 10)\n * @param {Number} m23 Component in column 2, row 3 position (index 11)\n * @param {Number} m30 Component in column 3, row 0 position (index 12)\n * @param {Number} m31 Component in column 3, row 1 position (index 13)\n * @param {Number} m32 Component in column 3, row 2 position (index 14)\n * @param {Number} m33 Component in column 3, row 3 position (index 15)\n * @returns {mat4} out\n */\nexport function set(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {\n  out[0] = m00;\n  out[1] = m01;\n  out[2] = m02;\n  out[3] = m03;\n  out[4] = m10;\n  out[5] = m11;\n  out[6] = m12;\n  out[7] = m13;\n  out[8] = m20;\n  out[9] = m21;\n  out[10] = m22;\n  out[11] = m23;\n  out[12] = m30;\n  out[13] = m31;\n  out[14] = m32;\n  out[15] = m33;\n  return out;\n}\n\n\n/**\n * Set a mat4 to the identity matrix\n *\n * @param {mat4} out the receiving matrix\n * @returns {mat4} out\n */\nexport function identity(out) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = 1;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[10] = 1;\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Transpose the values of a mat4\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the source matrix\n * @returns {mat4} out\n */\nexport function transpose(out, a) {\n  // If we are transposing ourselves we can skip a few steps but have to cache some values\n  if (out === a) {\n    let a01 = a[1], a02 = a[2], a03 = a[3];\n    let a12 = a[6], a13 = a[7];\n    let a23 = a[11];\n\n    out[1] = a[4];\n    out[2] = a[8];\n    out[3] = a[12];\n    out[4] = a01;\n    out[6] = a[9];\n    out[7] = a[13];\n    out[8] = a02;\n    out[9] = a12;\n    out[11] = a[14];\n    out[12] = a03;\n    out[13] = a13;\n    out[14] = a23;\n  } else {\n    out[0] = a[0];\n    out[1] = a[4];\n    out[2] = a[8];\n    out[3] = a[12];\n    out[4] = a[1];\n    out[5] = a[5];\n    out[6] = a[9];\n    out[7] = a[13];\n    out[8] = a[2];\n    out[9] = a[6];\n    out[10] = a[10];\n    out[11] = a[14];\n    out[12] = a[3];\n    out[13] = a[7];\n    out[14] = a[11];\n    out[15] = a[15];\n  }\n\n  return out;\n}\n\n/**\n * Inverts a mat4\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the source matrix\n * @returns {mat4} out\n */\nexport function invert(out, a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n\n  let b00 = a00 * a11 - a01 * a10;\n  let b01 = a00 * a12 - a02 * a10;\n  let b02 = a00 * a13 - a03 * a10;\n  let b03 = a01 * a12 - a02 * a11;\n  let b04 = a01 * a13 - a03 * a11;\n  let b05 = a02 * a13 - a03 * a12;\n  let b06 = a20 * a31 - a21 * a30;\n  let b07 = a20 * a32 - a22 * a30;\n  let b08 = a20 * a33 - a23 * a30;\n  let b09 = a21 * a32 - a22 * a31;\n  let b10 = a21 * a33 - a23 * a31;\n  let b11 = a22 * a33 - a23 * a32;\n\n  // Calculate the determinant\n  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n\n  if (!det) {\n    return null;\n  }\n  det = 1.0 / det;\n\n  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;\n  out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;\n  out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;\n  out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;\n  out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;\n  out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;\n  out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;\n  out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;\n  out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;\n  out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;\n  out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;\n  out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;\n  out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;\n  out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;\n  out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;\n  out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;\n\n  return out;\n}\n\n/**\n * Calculates the adjugate of a mat4\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the source matrix\n * @returns {mat4} out\n */\nexport function adjoint(out, a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n\n  out[0]  =  (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));\n  out[1]  = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));\n  out[2]  =  (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));\n  out[3]  = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));\n  out[4]  = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));\n  out[5]  =  (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));\n  out[6]  = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));\n  out[7]  =  (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));\n  out[8]  =  (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));\n  out[9]  = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));\n  out[10] =  (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));\n  out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));\n  out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));\n  out[13] =  (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));\n  out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));\n  out[15] =  (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));\n  return out;\n}\n\n/**\n * Calculates the determinant of a mat4\n *\n * @param {mat4} a the source matrix\n * @returns {Number} determinant of a\n */\nexport function determinant(a) {\n  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n\n  let b00 = a00 * a11 - a01 * a10;\n  let b01 = a00 * a12 - a02 * a10;\n  let b02 = a00 * a13 - a03 * a10;\n  let b03 = a01 * a12 - a02 * a11;\n  let b04 = a01 * a13 - a03 * a11;\n  let b05 = a02 * a13 - a03 * a12;\n  let b06 = a20 * a31 - a21 * a30;\n  let b07 = a20 * a32 - a22 * a30;\n  let b08 = a20 * a33 - a23 * a30;\n  let b09 = a21 * a32 - a22 * a31;\n  let b10 = a21 * a33 - a23 * a31;\n  let b11 = a22 * a33 - a23 * a32;\n\n  // Calculate the determinant\n  return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n}\n\n/**\n * Multiplies two mat4s\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the first operand\n * @param {mat4} b the second operand\n * @returns {mat4} out\n */\nexport function multiply(out, a, b) {\n  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n\n  // Cache only the current line of the second matrix\n  let b0  = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n  out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;\n  out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;\n  out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;\n  out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;\n\n  b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];\n  out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;\n  out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;\n  out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;\n  out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;\n\n  b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];\n  out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;\n  out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;\n  out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;\n  out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;\n\n  b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];\n  out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;\n  out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;\n  out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;\n  out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;\n  return out;\n}\n\n/**\n * Translate a mat4 by the given vector\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to translate\n * @param {vec3} v vector to translate by\n * @returns {mat4} out\n */\nexport function translate(out, a, v) {\n  let x = v[0], y = v[1], z = v[2];\n  let a00, a01, a02, a03;\n  let a10, a11, a12, a13;\n  let a20, a21, a22, a23;\n\n  if (a === out) {\n    out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];\n    out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];\n    out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];\n    out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];\n  } else {\n    a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];\n    a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];\n    a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];\n\n    out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;\n    out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;\n    out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;\n\n    out[12] = a00 * x + a10 * y + a20 * z + a[12];\n    out[13] = a01 * x + a11 * y + a21 * z + a[13];\n    out[14] = a02 * x + a12 * y + a22 * z + a[14];\n    out[15] = a03 * x + a13 * y + a23 * z + a[15];\n  }\n\n  return out;\n}\n\n/**\n * Scales the mat4 by the dimensions in the given vec3 not using vectorization\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to scale\n * @param {vec3} v the vec3 to scale the matrix by\n * @returns {mat4} out\n **/\nexport function scale(out, a, v) {\n  let x = v[0], y = v[1], z = v[2];\n\n  out[0] = a[0] * x;\n  out[1] = a[1] * x;\n  out[2] = a[2] * x;\n  out[3] = a[3] * x;\n  out[4] = a[4] * y;\n  out[5] = a[5] * y;\n  out[6] = a[6] * y;\n  out[7] = a[7] * y;\n  out[8] = a[8] * z;\n  out[9] = a[9] * z;\n  out[10] = a[10] * z;\n  out[11] = a[11] * z;\n  out[12] = a[12];\n  out[13] = a[13];\n  out[14] = a[14];\n  out[15] = a[15];\n  return out;\n}\n\n/**\n * Rotates a mat4 by the given angle around the given axis\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @param {vec3} axis the axis to rotate around\n * @returns {mat4} out\n */\nexport function rotate(out, a, rad, axis) {\n  let x = axis[0], y = axis[1], z = axis[2];\n  let len = Math.sqrt(x * x + y * y + z * z);\n  let s, c, t;\n  let a00, a01, a02, a03;\n  let a10, a11, a12, a13;\n  let a20, a21, a22, a23;\n  let b00, b01, b02;\n  let b10, b11, b12;\n  let b20, b21, b22;\n\n  if (len < glMatrix.EPSILON) { return null; }\n\n  len = 1 / len;\n  x *= len;\n  y *= len;\n  z *= len;\n\n  s = Math.sin(rad);\n  c = Math.cos(rad);\n  t = 1 - c;\n\n  a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];\n  a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];\n  a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];\n\n  // Construct the elements of the rotation matrix\n  b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;\n  b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;\n  b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;\n\n  // Perform rotation-specific matrix multiplication\n  out[0] = a00 * b00 + a10 * b01 + a20 * b02;\n  out[1] = a01 * b00 + a11 * b01 + a21 * b02;\n  out[2] = a02 * b00 + a12 * b01 + a22 * b02;\n  out[3] = a03 * b00 + a13 * b01 + a23 * b02;\n  out[4] = a00 * b10 + a10 * b11 + a20 * b12;\n  out[5] = a01 * b10 + a11 * b11 + a21 * b12;\n  out[6] = a02 * b10 + a12 * b11 + a22 * b12;\n  out[7] = a03 * b10 + a13 * b11 + a23 * b12;\n  out[8] = a00 * b20 + a10 * b21 + a20 * b22;\n  out[9] = a01 * b20 + a11 * b21 + a21 * b22;\n  out[10] = a02 * b20 + a12 * b21 + a22 * b22;\n  out[11] = a03 * b20 + a13 * b21 + a23 * b22;\n\n  if (a !== out) { // If the source and destination differ, copy the unchanged last row\n    out[12] = a[12];\n    out[13] = a[13];\n    out[14] = a[14];\n    out[15] = a[15];\n  }\n  return out;\n}\n\n/**\n * Rotates a matrix by the given angle around the X axis\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function rotateX(out, a, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  let a10 = a[4];\n  let a11 = a[5];\n  let a12 = a[6];\n  let a13 = a[7];\n  let a20 = a[8];\n  let a21 = a[9];\n  let a22 = a[10];\n  let a23 = a[11];\n\n  if (a !== out) { // If the source and destination differ, copy the unchanged rows\n    out[0]  = a[0];\n    out[1]  = a[1];\n    out[2]  = a[2];\n    out[3]  = a[3];\n    out[12] = a[12];\n    out[13] = a[13];\n    out[14] = a[14];\n    out[15] = a[15];\n  }\n\n  // Perform axis-specific matrix multiplication\n  out[4] = a10 * c + a20 * s;\n  out[5] = a11 * c + a21 * s;\n  out[6] = a12 * c + a22 * s;\n  out[7] = a13 * c + a23 * s;\n  out[8] = a20 * c - a10 * s;\n  out[9] = a21 * c - a11 * s;\n  out[10] = a22 * c - a12 * s;\n  out[11] = a23 * c - a13 * s;\n  return out;\n}\n\n/**\n * Rotates a matrix by the given angle around the Y axis\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function rotateY(out, a, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  let a00 = a[0];\n  let a01 = a[1];\n  let a02 = a[2];\n  let a03 = a[3];\n  let a20 = a[8];\n  let a21 = a[9];\n  let a22 = a[10];\n  let a23 = a[11];\n\n  if (a !== out) { // If the source and destination differ, copy the unchanged rows\n    out[4]  = a[4];\n    out[5]  = a[5];\n    out[6]  = a[6];\n    out[7]  = a[7];\n    out[12] = a[12];\n    out[13] = a[13];\n    out[14] = a[14];\n    out[15] = a[15];\n  }\n\n  // Perform axis-specific matrix multiplication\n  out[0] = a00 * c - a20 * s;\n  out[1] = a01 * c - a21 * s;\n  out[2] = a02 * c - a22 * s;\n  out[3] = a03 * c - a23 * s;\n  out[8] = a00 * s + a20 * c;\n  out[9] = a01 * s + a21 * c;\n  out[10] = a02 * s + a22 * c;\n  out[11] = a03 * s + a23 * c;\n  return out;\n}\n\n/**\n * Rotates a matrix by the given angle around the Z axis\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to rotate\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function rotateZ(out, a, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n  let a00 = a[0];\n  let a01 = a[1];\n  let a02 = a[2];\n  let a03 = a[3];\n  let a10 = a[4];\n  let a11 = a[5];\n  let a12 = a[6];\n  let a13 = a[7];\n\n  if (a !== out) { // If the source and destination differ, copy the unchanged last row\n    out[8]  = a[8];\n    out[9]  = a[9];\n    out[10] = a[10];\n    out[11] = a[11];\n    out[12] = a[12];\n    out[13] = a[13];\n    out[14] = a[14];\n    out[15] = a[15];\n  }\n\n  // Perform axis-specific matrix multiplication\n  out[0] = a00 * c + a10 * s;\n  out[1] = a01 * c + a11 * s;\n  out[2] = a02 * c + a12 * s;\n  out[3] = a03 * c + a13 * s;\n  out[4] = a10 * c - a00 * s;\n  out[5] = a11 * c - a01 * s;\n  out[6] = a12 * c - a02 * s;\n  out[7] = a13 * c - a03 * s;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector translation\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.translate(dest, dest, vec);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {vec3} v Translation vector\n * @returns {mat4} out\n */\nexport function fromTranslation(out, v) {\n  out[0] = 1;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = 1;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[10] = 1;\n  out[11] = 0;\n  out[12] = v[0];\n  out[13] = v[1];\n  out[14] = v[2];\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from a vector scaling\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.scale(dest, dest, vec);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {vec3} v Scaling vector\n * @returns {mat4} out\n */\nexport function fromScaling(out, v) {\n  out[0] = v[0];\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = v[1];\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[10] = v[2];\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from a given angle around a given axis\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.rotate(dest, dest, rad, axis);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @param {vec3} axis the axis to rotate around\n * @returns {mat4} out\n */\nexport function fromRotation(out, rad, axis) {\n  let x = axis[0], y = axis[1], z = axis[2];\n  let len = Math.sqrt(x * x + y * y + z * z);\n  let s, c, t;\n\n  if (len < glMatrix.EPSILON) { return null; }\n\n  len = 1 / len;\n  x *= len;\n  y *= len;\n  z *= len;\n\n  s = Math.sin(rad);\n  c = Math.cos(rad);\n  t = 1 - c;\n\n  // Perform rotation-specific matrix multiplication\n  out[0] = x * x * t + c;\n  out[1] = y * x * t + z * s;\n  out[2] = z * x * t - y * s;\n  out[3] = 0;\n  out[4] = x * y * t - z * s;\n  out[5] = y * y * t + c;\n  out[6] = z * y * t + x * s;\n  out[7] = 0;\n  out[8] = x * z * t + y * s;\n  out[9] = y * z * t - x * s;\n  out[10] = z * z * t + c;\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from the given angle around the X axis\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.rotateX(dest, dest, rad);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function fromXRotation(out, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n\n  // Perform axis-specific matrix multiplication\n  out[0]  = 1;\n  out[1]  = 0;\n  out[2]  = 0;\n  out[3]  = 0;\n  out[4] = 0;\n  out[5] = c;\n  out[6] = s;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = -s;\n  out[10] = c;\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from the given angle around the Y axis\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.rotateY(dest, dest, rad);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function fromYRotation(out, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n\n  // Perform axis-specific matrix multiplication\n  out[0]  = c;\n  out[1]  = 0;\n  out[2]  = -s;\n  out[3]  = 0;\n  out[4] = 0;\n  out[5] = 1;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = s;\n  out[9] = 0;\n  out[10] = c;\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from the given angle around the Z axis\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.rotateZ(dest, dest, rad);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {Number} rad the angle to rotate the matrix by\n * @returns {mat4} out\n */\nexport function fromZRotation(out, rad) {\n  let s = Math.sin(rad);\n  let c = Math.cos(rad);\n\n  // Perform axis-specific matrix multiplication\n  out[0]  = c;\n  out[1]  = s;\n  out[2]  = 0;\n  out[3]  = 0;\n  out[4] = -s;\n  out[5] = c;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[10] = 1;\n  out[11] = 0;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Creates a matrix from a quaternion rotation and vector translation\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.translate(dest, vec);\n *     let quatMat = mat4.create();\n *     quat4.toMat4(quat, quatMat);\n *     mat4.multiply(dest, quatMat);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {quat4} q Rotation quaternion\n * @param {vec3} v Translation vector\n * @returns {mat4} out\n */\nexport function fromRotationTranslation(out, q, v) {\n  // Quaternion math\n  let x = q[0], y = q[1], z = q[2], w = q[3];\n  let x2 = x + x;\n  let y2 = y + y;\n  let z2 = z + z;\n\n  let xx = x * x2;\n  let xy = x * y2;\n  let xz = x * z2;\n  let yy = y * y2;\n  let yz = y * z2;\n  let zz = z * z2;\n  let wx = w * x2;\n  let wy = w * y2;\n  let wz = w * z2;\n\n  out[0] = 1 - (yy + zz);\n  out[1] = xy + wz;\n  out[2] = xz - wy;\n  out[3] = 0;\n  out[4] = xy - wz;\n  out[5] = 1 - (xx + zz);\n  out[6] = yz + wx;\n  out[7] = 0;\n  out[8] = xz + wy;\n  out[9] = yz - wx;\n  out[10] = 1 - (xx + yy);\n  out[11] = 0;\n  out[12] = v[0];\n  out[13] = v[1];\n  out[14] = v[2];\n  out[15] = 1;\n\n  return out;\n}\n\n/**\n * Creates a new mat4 from a dual quat.\n *\n * @param {mat4} out Matrix\n * @param {quat2} a Dual Quaternion\n * @returns {mat4} mat4 receiving operation result\n */\nexport function fromQuat2(out, a) {\n  let translation = new glMatrix.ARRAY_TYPE(3);\n  let bx = -a[0], by = -a[1], bz = -a[2], bw = a[3],\n  ax = a[4], ay = a[5], az = a[6], aw = a[7];\n\n  let magnitude = bx * bx + by * by + bz * bz + bw * bw;\n  //Only scale if it makes sense\n  if (magnitude > 0) {\n    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;\n    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;\n    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;\n  } else {\n    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;\n    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;\n    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;\n  }\n  fromRotationTranslation(out, a, translation);\n  return out;\n}\n\n/**\n * Returns the translation vector component of a transformation\n *  matrix. If a matrix is built with fromRotationTranslation,\n *  the returned vector will be the same as the translation vector\n *  originally supplied.\n * @param  {vec3} out Vector to receive translation component\n * @param  {mat4} mat Matrix to be decomposed (input)\n * @return {vec3} out\n */\nexport function getTranslation(out, mat) {\n  out[0] = mat[12];\n  out[1] = mat[13];\n  out[2] = mat[14];\n\n  return out;\n}\n\n/**\n * Returns the scaling factor component of a transformation\n *  matrix. If a matrix is built with fromRotationTranslationScale\n *  with a normalized Quaternion paramter, the returned vector will be\n *  the same as the scaling vector\n *  originally supplied.\n * @param  {vec3} out Vector to receive scaling factor component\n * @param  {mat4} mat Matrix to be decomposed (input)\n * @return {vec3} out\n */\nexport function getScaling(out, mat) {\n  let m11 = mat[0];\n  let m12 = mat[1];\n  let m13 = mat[2];\n  let m21 = mat[4];\n  let m22 = mat[5];\n  let m23 = mat[6];\n  let m31 = mat[8];\n  let m32 = mat[9];\n  let m33 = mat[10];\n\n  out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);\n  out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);\n  out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);\n\n  return out;\n}\n\n/**\n * Returns a quaternion representing the rotational component\n *  of a transformation matrix. If a matrix is built with\n *  fromRotationTranslation, the returned quaternion will be the\n *  same as the quaternion originally supplied.\n * @param {quat} out Quaternion to receive the rotation component\n * @param {mat4} mat Matrix to be decomposed (input)\n * @return {quat} out\n */\nexport function getRotation(out, mat) {\n  // Algorithm taken from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n  let trace = mat[0] + mat[5] + mat[10];\n  let S = 0;\n\n  if (trace > 0) {\n    S = Math.sqrt(trace + 1.0) * 2;\n    out[3] = 0.25 * S;\n    out[0] = (mat[6] - mat[9]) / S;\n    out[1] = (mat[8] - mat[2]) / S;\n    out[2] = (mat[1] - mat[4]) / S;\n  } else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) {\n    S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2;\n    out[3] = (mat[6] - mat[9]) / S;\n    out[0] = 0.25 * S;\n    out[1] = (mat[1] + mat[4]) / S;\n    out[2] = (mat[8] + mat[2]) / S;\n  } else if (mat[5] > mat[10]) {\n    S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2;\n    out[3] = (mat[8] - mat[2]) / S;\n    out[0] = (mat[1] + mat[4]) / S;\n    out[1] = 0.25 * S;\n    out[2] = (mat[6] + mat[9]) / S;\n  } else {\n    S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2;\n    out[3] = (mat[1] - mat[4]) / S;\n    out[0] = (mat[8] + mat[2]) / S;\n    out[1] = (mat[6] + mat[9]) / S;\n    out[2] = 0.25 * S;\n  }\n\n  return out;\n}\n\n/**\n * Creates a matrix from a quaternion rotation, vector translation and vector scale\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.translate(dest, vec);\n *     let quatMat = mat4.create();\n *     quat4.toMat4(quat, quatMat);\n *     mat4.multiply(dest, quatMat);\n *     mat4.scale(dest, scale)\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {quat4} q Rotation quaternion\n * @param {vec3} v Translation vector\n * @param {vec3} s Scaling vector\n * @returns {mat4} out\n */\nexport function fromRotationTranslationScale(out, q, v, s) {\n  // Quaternion math\n  let x = q[0], y = q[1], z = q[2], w = q[3];\n  let x2 = x + x;\n  let y2 = y + y;\n  let z2 = z + z;\n\n  let xx = x * x2;\n  let xy = x * y2;\n  let xz = x * z2;\n  let yy = y * y2;\n  let yz = y * z2;\n  let zz = z * z2;\n  let wx = w * x2;\n  let wy = w * y2;\n  let wz = w * z2;\n  let sx = s[0];\n  let sy = s[1];\n  let sz = s[2];\n\n  out[0] = (1 - (yy + zz)) * sx;\n  out[1] = (xy + wz) * sx;\n  out[2] = (xz - wy) * sx;\n  out[3] = 0;\n  out[4] = (xy - wz) * sy;\n  out[5] = (1 - (xx + zz)) * sy;\n  out[6] = (yz + wx) * sy;\n  out[7] = 0;\n  out[8] = (xz + wy) * sz;\n  out[9] = (yz - wx) * sz;\n  out[10] = (1 - (xx + yy)) * sz;\n  out[11] = 0;\n  out[12] = v[0];\n  out[13] = v[1];\n  out[14] = v[2];\n  out[15] = 1;\n\n  return out;\n}\n\n/**\n * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin\n * This is equivalent to (but much faster than):\n *\n *     mat4.identity(dest);\n *     mat4.translate(dest, vec);\n *     mat4.translate(dest, origin);\n *     let quatMat = mat4.create();\n *     quat4.toMat4(quat, quatMat);\n *     mat4.multiply(dest, quatMat);\n *     mat4.scale(dest, scale)\n *     mat4.translate(dest, negativeOrigin);\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {quat4} q Rotation quaternion\n * @param {vec3} v Translation vector\n * @param {vec3} s Scaling vector\n * @param {vec3} o The origin vector around which to scale and rotate\n * @returns {mat4} out\n */\nexport function fromRotationTranslationScaleOrigin(out, q, v, s, o) {\n  // Quaternion math\n  let x = q[0], y = q[1], z = q[2], w = q[3];\n  let x2 = x + x;\n  let y2 = y + y;\n  let z2 = z + z;\n\n  let xx = x * x2;\n  let xy = x * y2;\n  let xz = x * z2;\n  let yy = y * y2;\n  let yz = y * z2;\n  let zz = z * z2;\n  let wx = w * x2;\n  let wy = w * y2;\n  let wz = w * z2;\n\n  let sx = s[0];\n  let sy = s[1];\n  let sz = s[2];\n\n  let ox = o[0];\n  let oy = o[1];\n  let oz = o[2];\n\n  let out0 = (1 - (yy + zz)) * sx;\n  let out1 = (xy + wz) * sx;\n  let out2 = (xz - wy) * sx;\n  let out4 = (xy - wz) * sy;\n  let out5 = (1 - (xx + zz)) * sy;\n  let out6 = (yz + wx) * sy;\n  let out8 = (xz + wy) * sz;\n  let out9 = (yz - wx) * sz;\n  let out10 = (1 - (xx + yy)) * sz;\n\n  out[0] = out0;\n  out[1] = out1;\n  out[2] = out2;\n  out[3] = 0;\n  out[4] = out4;\n  out[5] = out5;\n  out[6] = out6;\n  out[7] = 0;\n  out[8] = out8;\n  out[9] = out9;\n  out[10] = out10;\n  out[11] = 0;\n  out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);\n  out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);\n  out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);\n  out[15] = 1;\n\n  return out;\n}\n\n/**\n * Calculates a 4x4 matrix from the given quaternion\n *\n * @param {mat4} out mat4 receiving operation result\n * @param {quat} q Quaternion to create matrix from\n *\n * @returns {mat4} out\n */\nexport function fromQuat(out, q) {\n  let x = q[0], y = q[1], z = q[2], w = q[3];\n  let x2 = x + x;\n  let y2 = y + y;\n  let z2 = z + z;\n\n  let xx = x * x2;\n  let yx = y * x2;\n  let yy = y * y2;\n  let zx = z * x2;\n  let zy = z * y2;\n  let zz = z * z2;\n  let wx = w * x2;\n  let wy = w * y2;\n  let wz = w * z2;\n\n  out[0] = 1 - yy - zz;\n  out[1] = yx + wz;\n  out[2] = zx - wy;\n  out[3] = 0;\n\n  out[4] = yx - wz;\n  out[5] = 1 - xx - zz;\n  out[6] = zy + wx;\n  out[7] = 0;\n\n  out[8] = zx + wy;\n  out[9] = zy - wx;\n  out[10] = 1 - xx - yy;\n  out[11] = 0;\n\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = 0;\n  out[15] = 1;\n\n  return out;\n}\n\n/**\n * Generates a frustum matrix with the given bounds\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {Number} left Left bound of the frustum\n * @param {Number} right Right bound of the frustum\n * @param {Number} bottom Bottom bound of the frustum\n * @param {Number} top Top bound of the frustum\n * @param {Number} near Near bound of the frustum\n * @param {Number} far Far bound of the frustum\n * @returns {mat4} out\n */\nexport function frustum(out, left, right, bottom, top, near, far) {\n  let rl = 1 / (right - left);\n  let tb = 1 / (top - bottom);\n  let nf = 1 / (near - far);\n  out[0] = (near * 2) * rl;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = (near * 2) * tb;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = (right + left) * rl;\n  out[9] = (top + bottom) * tb;\n  out[10] = (far + near) * nf;\n  out[11] = -1;\n  out[12] = 0;\n  out[13] = 0;\n  out[14] = (far * near * 2) * nf;\n  out[15] = 0;\n  return out;\n}\n\n/**\n * Generates a perspective projection matrix with the given bounds.\n * Passing null/undefined/no value for far will generate infinite projection matrix.\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {number} fovy Vertical field of view in radians\n * @param {number} aspect Aspect ratio. typically viewport width/height\n * @param {number} near Near bound of the frustum\n * @param {number} far Far bound of the frustum, can be null or Infinity\n * @returns {mat4} out\n */\nexport function perspective(out, fovy, aspect, near, far) {\n  let f = 1.0 / Math.tan(fovy / 2), nf;\n  out[0] = f / aspect;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = f;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[11] = -1;\n  out[12] = 0;\n  out[13] = 0;\n  out[15] = 0;\n  if (far != null && far !== Infinity) {\n    nf = 1 / (near - far);\n    out[10] = (far + near) * nf;\n    out[14] = (2 * far * near) * nf;\n  } else {\n    out[10] = -1;\n    out[14] = -2 * near;\n  }\n  return out;\n}\n\n/**\n * Generates a perspective projection matrix with the given field of view.\n * This is primarily useful for generating projection matrices to be used\n * with the still experiemental WebVR API.\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees\n * @param {number} near Near bound of the frustum\n * @param {number} far Far bound of the frustum\n * @returns {mat4} out\n */\nexport function perspectiveFromFieldOfView(out, fov, near, far) {\n  let upTan = Math.tan(fov.upDegrees * Math.PI/180.0);\n  let downTan = Math.tan(fov.downDegrees * Math.PI/180.0);\n  let leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0);\n  let rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0);\n  let xScale = 2.0 / (leftTan + rightTan);\n  let yScale = 2.0 / (upTan + downTan);\n\n  out[0] = xScale;\n  out[1] = 0.0;\n  out[2] = 0.0;\n  out[3] = 0.0;\n  out[4] = 0.0;\n  out[5] = yScale;\n  out[6] = 0.0;\n  out[7] = 0.0;\n  out[8] = -((leftTan - rightTan) * xScale * 0.5);\n  out[9] = ((upTan - downTan) * yScale * 0.5);\n  out[10] = far / (near - far);\n  out[11] = -1.0;\n  out[12] = 0.0;\n  out[13] = 0.0;\n  out[14] = (far * near) / (near - far);\n  out[15] = 0.0;\n  return out;\n}\n\n/**\n * Generates a orthogonal projection matrix with the given bounds\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {number} left Left bound of the frustum\n * @param {number} right Right bound of the frustum\n * @param {number} bottom Bottom bound of the frustum\n * @param {number} top Top bound of the frustum\n * @param {number} near Near bound of the frustum\n * @param {number} far Far bound of the frustum\n * @returns {mat4} out\n */\nexport function ortho(out, left, right, bottom, top, near, far) {\n  let lr = 1 / (left - right);\n  let bt = 1 / (bottom - top);\n  let nf = 1 / (near - far);\n  out[0] = -2 * lr;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 0;\n  out[4] = 0;\n  out[5] = -2 * bt;\n  out[6] = 0;\n  out[7] = 0;\n  out[8] = 0;\n  out[9] = 0;\n  out[10] = 2 * nf;\n  out[11] = 0;\n  out[12] = (left + right) * lr;\n  out[13] = (top + bottom) * bt;\n  out[14] = (far + near) * nf;\n  out[15] = 1;\n  return out;\n}\n\n/**\n * Generates a look-at matrix with the given eye position, focal point, and up axis.\n * If you want a matrix that actually makes an object look at another object, you should use targetTo instead.\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {vec3} eye Position of the viewer\n * @param {vec3} center Point the viewer is looking at\n * @param {vec3} up vec3 pointing up\n * @returns {mat4} out\n */\nexport function lookAt(out, eye, center, up) {\n  let x0, x1, x2, y0, y1, y2, z0, z1, z2, len;\n  let eyex = eye[0];\n  let eyey = eye[1];\n  let eyez = eye[2];\n  let upx = up[0];\n  let upy = up[1];\n  let upz = up[2];\n  let centerx = center[0];\n  let centery = center[1];\n  let centerz = center[2];\n\n  if (Math.abs(eyex - centerx) < glMatrix.EPSILON &&\n      Math.abs(eyey - centery) < glMatrix.EPSILON &&\n      Math.abs(eyez - centerz) < glMatrix.EPSILON) {\n    return identity(out);\n  }\n\n  z0 = eyex - centerx;\n  z1 = eyey - centery;\n  z2 = eyez - centerz;\n\n  len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);\n  z0 *= len;\n  z1 *= len;\n  z2 *= len;\n\n  x0 = upy * z2 - upz * z1;\n  x1 = upz * z0 - upx * z2;\n  x2 = upx * z1 - upy * z0;\n  len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);\n  if (!len) {\n    x0 = 0;\n    x1 = 0;\n    x2 = 0;\n  } else {\n    len = 1 / len;\n    x0 *= len;\n    x1 *= len;\n    x2 *= len;\n  }\n\n  y0 = z1 * x2 - z2 * x1;\n  y1 = z2 * x0 - z0 * x2;\n  y2 = z0 * x1 - z1 * x0;\n\n  len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);\n  if (!len) {\n    y0 = 0;\n    y1 = 0;\n    y2 = 0;\n  } else {\n    len = 1 / len;\n    y0 *= len;\n    y1 *= len;\n    y2 *= len;\n  }\n\n  out[0] = x0;\n  out[1] = y0;\n  out[2] = z0;\n  out[3] = 0;\n  out[4] = x1;\n  out[5] = y1;\n  out[6] = z1;\n  out[7] = 0;\n  out[8] = x2;\n  out[9] = y2;\n  out[10] = z2;\n  out[11] = 0;\n  out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);\n  out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);\n  out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);\n  out[15] = 1;\n\n  return out;\n}\n\n/**\n * Generates a matrix that makes something look at something else.\n *\n * @param {mat4} out mat4 frustum matrix will be written into\n * @param {vec3} eye Position of the viewer\n * @param {vec3} center Point the viewer is looking at\n * @param {vec3} up vec3 pointing up\n * @returns {mat4} out\n */\nexport function targetTo(out, eye, target, up) {\n  let eyex = eye[0],\n      eyey = eye[1],\n      eyez = eye[2],\n      upx = up[0],\n      upy = up[1],\n      upz = up[2];\n\n  let z0 = eyex - target[0],\n      z1 = eyey - target[1],\n      z2 = eyez - target[2];\n\n  let len = z0*z0 + z1*z1 + z2*z2;\n  if (len > 0) {\n    len = 1 / Math.sqrt(len);\n    z0 *= len;\n    z1 *= len;\n    z2 *= len;\n  }\n\n  let x0 = upy * z2 - upz * z1,\n      x1 = upz * z0 - upx * z2,\n      x2 = upx * z1 - upy * z0;\n\n  len = x0*x0 + x1*x1 + x2*x2;\n  if (len > 0) {\n    len = 1 / Math.sqrt(len);\n    x0 *= len;\n    x1 *= len;\n    x2 *= len;\n  }\n\n  out[0] = x0;\n  out[1] = x1;\n  out[2] = x2;\n  out[3] = 0;\n  out[4] = z1 * x2 - z2 * x1;\n  out[5] = z2 * x0 - z0 * x2;\n  out[6] = z0 * x1 - z1 * x0;\n  out[7] = 0;\n  out[8] = z0;\n  out[9] = z1;\n  out[10] = z2;\n  out[11] = 0;\n  out[12] = eyex;\n  out[13] = eyey;\n  out[14] = eyez;\n  out[15] = 1;\n  return out;\n};\n\n/**\n * Returns a string representation of a mat4\n *\n * @param {mat4} a matrix to represent as a string\n * @returns {String} string representation of the matrix\n */\nexport function str(a) {\n  return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +\n          a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +\n          a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +\n          a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';\n}\n\n/**\n * Returns Frobenius norm of a mat4\n *\n * @param {mat4} a the matrix to calculate Frobenius norm of\n * @returns {Number} Frobenius norm\n */\nexport function frob(a) {\n  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2) + Math.pow(a[9], 2) + Math.pow(a[10], 2) + Math.pow(a[11], 2) + Math.pow(a[12], 2) + Math.pow(a[13], 2) + Math.pow(a[14], 2) + Math.pow(a[15], 2) ))\n}\n\n/**\n * Adds two mat4's\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the first operand\n * @param {mat4} b the second operand\n * @returns {mat4} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  out[4] = a[4] + b[4];\n  out[5] = a[5] + b[5];\n  out[6] = a[6] + b[6];\n  out[7] = a[7] + b[7];\n  out[8] = a[8] + b[8];\n  out[9] = a[9] + b[9];\n  out[10] = a[10] + b[10];\n  out[11] = a[11] + b[11];\n  out[12] = a[12] + b[12];\n  out[13] = a[13] + b[13];\n  out[14] = a[14] + b[14];\n  out[15] = a[15] + b[15];\n  return out;\n}\n\n/**\n * Subtracts matrix b from matrix a\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the first operand\n * @param {mat4} b the second operand\n * @returns {mat4} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  out[3] = a[3] - b[3];\n  out[4] = a[4] - b[4];\n  out[5] = a[5] - b[5];\n  out[6] = a[6] - b[6];\n  out[7] = a[7] - b[7];\n  out[8] = a[8] - b[8];\n  out[9] = a[9] - b[9];\n  out[10] = a[10] - b[10];\n  out[11] = a[11] - b[11];\n  out[12] = a[12] - b[12];\n  out[13] = a[13] - b[13];\n  out[14] = a[14] - b[14];\n  out[15] = a[15] - b[15];\n  return out;\n}\n\n/**\n * Multiply each element of the matrix by a scalar.\n *\n * @param {mat4} out the receiving matrix\n * @param {mat4} a the matrix to scale\n * @param {Number} b amount to scale the matrix's elements by\n * @returns {mat4} out\n */\nexport function multiplyScalar(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  out[4] = a[4] * b;\n  out[5] = a[5] * b;\n  out[6] = a[6] * b;\n  out[7] = a[7] * b;\n  out[8] = a[8] * b;\n  out[9] = a[9] * b;\n  out[10] = a[10] * b;\n  out[11] = a[11] * b;\n  out[12] = a[12] * b;\n  out[13] = a[13] * b;\n  out[14] = a[14] * b;\n  out[15] = a[15] * b;\n  return out;\n}\n\n/**\n * Adds two mat4's after multiplying each element of the second operand by a scalar value.\n *\n * @param {mat4} out the receiving vector\n * @param {mat4} a the first operand\n * @param {mat4} b the second operand\n * @param {Number} scale the amount to scale b's elements by before adding\n * @returns {mat4} out\n */\nexport function multiplyScalarAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  out[3] = a[3] + (b[3] * scale);\n  out[4] = a[4] + (b[4] * scale);\n  out[5] = a[5] + (b[5] * scale);\n  out[6] = a[6] + (b[6] * scale);\n  out[7] = a[7] + (b[7] * scale);\n  out[8] = a[8] + (b[8] * scale);\n  out[9] = a[9] + (b[9] * scale);\n  out[10] = a[10] + (b[10] * scale);\n  out[11] = a[11] + (b[11] * scale);\n  out[12] = a[12] + (b[12] * scale);\n  out[13] = a[13] + (b[13] * scale);\n  out[14] = a[14] + (b[14] * scale);\n  out[15] = a[15] + (b[15] * scale);\n  return out;\n}\n\n/**\n * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)\n *\n * @param {mat4} a The first matrix.\n * @param {mat4} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&\n         a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] &&\n         a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] &&\n         a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];\n}\n\n/**\n * Returns whether or not the matrices have approximately the same elements in the same position.\n *\n * @param {mat4} a The first matrix.\n * @param {mat4} b The second matrix.\n * @returns {Boolean} True if the matrices are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0  = a[0],  a1  = a[1],  a2  = a[2],  a3  = a[3];\n  let a4  = a[4],  a5  = a[5],  a6  = a[6],  a7  = a[7];\n  let a8  = a[8],  a9  = a[9],  a10 = a[10], a11 = a[11];\n  let a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];\n\n  let b0  = b[0],  b1  = b[1],  b2  = b[2],  b3  = b[3];\n  let b4  = b[4],  b5  = b[5],  b6  = b[6],  b7  = b[7];\n  let b8  = b[8],  b9  = b[9],  b10 = b[10], b11 = b[11];\n  let b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];\n\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&\n          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&\n          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&\n          Math.abs(a6 - b6) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&\n          Math.abs(a7 - b7) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&\n          Math.abs(a8 - b8) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a8), Math.abs(b8)) &&\n          Math.abs(a9 - b9) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a9), Math.abs(b9)) &&\n          Math.abs(a10 - b10) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a10), Math.abs(b10)) &&\n          Math.abs(a11 - b11) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a11), Math.abs(b11)) &&\n          Math.abs(a12 - b12) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a12), Math.abs(b12)) &&\n          Math.abs(a13 - b13) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a13), Math.abs(b13)) &&\n          Math.abs(a14 - b14) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a14), Math.abs(b14)) &&\n          Math.abs(a15 - b15) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a15), Math.abs(b15)));\n}\n\n/**\n * Alias for {@link mat4.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link mat4.subtract}\n * @function\n */\nexport const sub = subtract;\n","import * as glMatrix from \"./common.js\"\nimport * as mat3 from \"./mat3.js\"\nimport * as vec3 from \"./vec3.js\"\nimport * as vec4 from \"./vec4.js\"\n\n/**\n * Quaternion\n * @module quat\n */\n\n/**\n * Creates a new identity quat\n *\n * @returns {quat} a new quaternion\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[0] = 0;\n    out[1] = 0;\n    out[2] = 0;\n  }\n  out[3] = 1;\n  return out;\n}\n\n/**\n * Set a quat to the identity quaternion\n *\n * @param {quat} out the receiving quaternion\n * @returns {quat} out\n */\nexport function identity(out) {\n  out[0] = 0;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  return out;\n}\n\n/**\n * Sets a quat from the given angle and rotation axis,\n * then returns it.\n *\n * @param {quat} out the receiving quaternion\n * @param {vec3} axis the axis around which to rotate\n * @param {Number} rad the angle in radians\n * @returns {quat} out\n **/\nexport function setAxisAngle(out, axis, rad) {\n  rad = rad * 0.5;\n  let s = Math.sin(rad);\n  out[0] = s * axis[0];\n  out[1] = s * axis[1];\n  out[2] = s * axis[2];\n  out[3] = Math.cos(rad);\n  return out;\n}\n\n/**\n * Gets the rotation axis and angle for a given\n *  quaternion. If a quaternion is created with\n *  setAxisAngle, this method will return the same\n *  values as providied in the original parameter list\n *  OR functionally equivalent values.\n * Example: The quaternion formed by axis [0, 0, 1] and\n *  angle -90 is the same as the quaternion formed by\n *  [0, 0, 1] and 270. This method favors the latter.\n * @param  {vec3} out_axis  Vector receiving the axis of rotation\n * @param  {quat} q     Quaternion to be decomposed\n * @return {Number}     Angle, in radians, of the rotation\n */\nexport function getAxisAngle(out_axis, q) {\n  let rad = Math.acos(q[3]) * 2.0;\n  let s = Math.sin(rad / 2.0);\n  if (s > glMatrix.EPSILON) {\n    out_axis[0] = q[0] / s;\n    out_axis[1] = q[1] / s;\n    out_axis[2] = q[2] / s;\n  } else {\n    // If s is zero, return any axis (no rotation - axis does not matter)\n    out_axis[0] = 1;\n    out_axis[1] = 0;\n    out_axis[2] = 0;\n  }\n  return rad;\n}\n\n/**\n * Multiplies two quat's\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @returns {quat} out\n */\nexport function multiply(out, a, b) {\n  let ax = a[0], ay = a[1], az = a[2], aw = a[3];\n  let bx = b[0], by = b[1], bz = b[2], bw = b[3];\n\n  out[0] = ax * bw + aw * bx + ay * bz - az * by;\n  out[1] = ay * bw + aw * by + az * bx - ax * bz;\n  out[2] = az * bw + aw * bz + ax * by - ay * bx;\n  out[3] = aw * bw - ax * bx - ay * by - az * bz;\n  return out;\n}\n\n/**\n * Rotates a quaternion by the given angle about the X axis\n *\n * @param {quat} out quat receiving operation result\n * @param {quat} a quat to rotate\n * @param {number} rad angle (in radians) to rotate\n * @returns {quat} out\n */\nexport function rotateX(out, a, rad) {\n  rad *= 0.5;\n\n  let ax = a[0], ay = a[1], az = a[2], aw = a[3];\n  let bx = Math.sin(rad), bw = Math.cos(rad);\n\n  out[0] = ax * bw + aw * bx;\n  out[1] = ay * bw + az * bx;\n  out[2] = az * bw - ay * bx;\n  out[3] = aw * bw - ax * bx;\n  return out;\n}\n\n/**\n * Rotates a quaternion by the given angle about the Y axis\n *\n * @param {quat} out quat receiving operation result\n * @param {quat} a quat to rotate\n * @param {number} rad angle (in radians) to rotate\n * @returns {quat} out\n */\nexport function rotateY(out, a, rad) {\n  rad *= 0.5;\n\n  let ax = a[0], ay = a[1], az = a[2], aw = a[3];\n  let by = Math.sin(rad), bw = Math.cos(rad);\n\n  out[0] = ax * bw - az * by;\n  out[1] = ay * bw + aw * by;\n  out[2] = az * bw + ax * by;\n  out[3] = aw * bw - ay * by;\n  return out;\n}\n\n/**\n * Rotates a quaternion by the given angle about the Z axis\n *\n * @param {quat} out quat receiving operation result\n * @param {quat} a quat to rotate\n * @param {number} rad angle (in radians) to rotate\n * @returns {quat} out\n */\nexport function rotateZ(out, a, rad) {\n  rad *= 0.5;\n\n  let ax = a[0], ay = a[1], az = a[2], aw = a[3];\n  let bz = Math.sin(rad), bw = Math.cos(rad);\n\n  out[0] = ax * bw + ay * bz;\n  out[1] = ay * bw - ax * bz;\n  out[2] = az * bw + aw * bz;\n  out[3] = aw * bw - az * bz;\n  return out;\n}\n\n/**\n * Calculates the W component of a quat from the X, Y, and Z components.\n * Assumes that quaternion is 1 unit in length.\n * Any existing W component will be ignored.\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a quat to calculate W component of\n * @returns {quat} out\n */\nexport function calculateW(out, a) {\n  let x = a[0], y = a[1], z = a[2];\n\n  out[0] = x;\n  out[1] = y;\n  out[2] = z;\n  out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));\n  return out;\n}\n\n/**\n * Performs a spherical linear interpolation between two quat\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {quat} out\n */\nexport function slerp(out, a, b, t) {\n  // benchmarks:\n  //    http://jsperf.com/quaternion-slerp-implementations\n  let ax = a[0], ay = a[1], az = a[2], aw = a[3];\n  let bx = b[0], by = b[1], bz = b[2], bw = b[3];\n\n  let omega, cosom, sinom, scale0, scale1;\n\n  // calc cosine\n  cosom = ax * bx + ay * by + az * bz + aw * bw;\n  // adjust signs (if necessary)\n  if ( cosom < 0.0 ) {\n    cosom = -cosom;\n    bx = - bx;\n    by = - by;\n    bz = - bz;\n    bw = - bw;\n  }\n  // calculate coefficients\n  if ( (1.0 - cosom) > glMatrix.EPSILON ) {\n    // standard case (slerp)\n    omega  = Math.acos(cosom);\n    sinom  = Math.sin(omega);\n    scale0 = Math.sin((1.0 - t) * omega) / sinom;\n    scale1 = Math.sin(t * omega) / sinom;\n  } else {\n    // \"from\" and \"to\" quaternions are very close\n    //  ... so we can do a linear interpolation\n    scale0 = 1.0 - t;\n    scale1 = t;\n  }\n  // calculate final values\n  out[0] = scale0 * ax + scale1 * bx;\n  out[1] = scale0 * ay + scale1 * by;\n  out[2] = scale0 * az + scale1 * bz;\n  out[3] = scale0 * aw + scale1 * bw;\n\n  return out;\n}\n\n/**\n * Generates a random quaternion\n *\n * @param {quat} out the receiving quaternion\n * @returns {quat} out\n */\nexport function random(out) {\n  // Implementation of http://planning.cs.uiuc.edu/node198.html\n  // TODO: Calling random 3 times is probably not the fastest solution\n  let u1 = glMatrix.RANDOM();\n  let u2 = glMatrix.RANDOM();\n  let u3 = glMatrix.RANDOM();\n\n  let sqrt1MinusU1 = Math.sqrt(1 - u1);\n  let sqrtU1 = Math.sqrt(u1);\n\n  out[0] = sqrt1MinusU1 * Math.sin(2.0 * Math.PI * u2);\n  out[1] = sqrt1MinusU1 * Math.cos(2.0 * Math.PI * u2);\n  out[2] = sqrtU1 * Math.sin(2.0 * Math.PI * u3);\n  out[3] = sqrtU1 * Math.cos(2.0 * Math.PI * u3);\n  return out;\n}\n\n/**\n * Calculates the inverse of a quat\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a quat to calculate inverse of\n * @returns {quat} out\n */\nexport function invert(out, a) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let dot = a0*a0 + a1*a1 + a2*a2 + a3*a3;\n  let invDot = dot ? 1.0/dot : 0;\n\n  // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0\n\n  out[0] = -a0*invDot;\n  out[1] = -a1*invDot;\n  out[2] = -a2*invDot;\n  out[3] = a3*invDot;\n  return out;\n}\n\n/**\n * Calculates the conjugate of a quat\n * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a quat to calculate conjugate of\n * @returns {quat} out\n */\nexport function conjugate(out, a) {\n  out[0] = -a[0];\n  out[1] = -a[1];\n  out[2] = -a[2];\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Creates a quaternion from the given 3x3 rotation matrix.\n *\n * NOTE: The resultant quaternion is not normalized, so you should be sure\n * to renormalize the quaternion yourself where necessary.\n *\n * @param {quat} out the receiving quaternion\n * @param {mat3} m rotation matrix\n * @returns {quat} out\n * @function\n */\nexport function fromMat3(out, m) {\n  // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes\n  // article \"Quaternion Calculus and Fast Animation\".\n  let fTrace = m[0] + m[4] + m[8];\n  let fRoot;\n\n  if ( fTrace > 0.0 ) {\n    // |w| > 1/2, may as well choose w > 1/2\n    fRoot = Math.sqrt(fTrace + 1.0);  // 2w\n    out[3] = 0.5 * fRoot;\n    fRoot = 0.5/fRoot;  // 1/(4w)\n    out[0] = (m[5]-m[7])*fRoot;\n    out[1] = (m[6]-m[2])*fRoot;\n    out[2] = (m[1]-m[3])*fRoot;\n  } else {\n    // |w| <= 1/2\n    let i = 0;\n    if ( m[4] > m[0] )\n      i = 1;\n    if ( m[8] > m[i*3+i] )\n      i = 2;\n    let j = (i+1)%3;\n    let k = (i+2)%3;\n\n    fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0);\n    out[i] = 0.5 * fRoot;\n    fRoot = 0.5 / fRoot;\n    out[3] = (m[j*3+k] - m[k*3+j]) * fRoot;\n    out[j] = (m[j*3+i] + m[i*3+j]) * fRoot;\n    out[k] = (m[k*3+i] + m[i*3+k]) * fRoot;\n  }\n\n  return out;\n}\n\n/**\n * Creates a quaternion from the given euler angle x, y, z.\n *\n * @param {quat} out the receiving quaternion\n * @param {x} Angle to rotate around X axis in degrees.\n * @param {y} Angle to rotate around Y axis in degrees.\n * @param {z} Angle to rotate around Z axis in degrees.\n * @returns {quat} out\n * @function\n */\nexport function fromEuler(out, x, y, z) {\n    let halfToRad = 0.5 * Math.PI / 180.0;\n    x *= halfToRad;\n    y *= halfToRad;\n    z *= halfToRad;\n\n    let sx = Math.sin(x);\n    let cx = Math.cos(x);\n    let sy = Math.sin(y);\n    let cy = Math.cos(y);\n    let sz = Math.sin(z);\n    let cz = Math.cos(z);\n\n    out[0] = sx * cy * cz - cx * sy * sz;\n    out[1] = cx * sy * cz + sx * cy * sz;\n    out[2] = cx * cy * sz - sx * sy * cz;\n    out[3] = cx * cy * cz + sx * sy * sz;\n\n    return out;\n}\n\n/**\n * Returns a string representation of a quatenion\n *\n * @param {quat} a vector to represent as a string\n * @returns {String} string representation of the vector\n */\nexport function str(a) {\n  return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';\n}\n\n/**\n * Creates a new quat initialized with values from an existing quaternion\n *\n * @param {quat} a quaternion to clone\n * @returns {quat} a new quaternion\n * @function\n */\nexport const clone = vec4.clone;\n\n/**\n * Creates a new quat initialized with the given values\n *\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @param {Number} w W component\n * @returns {quat} a new quaternion\n * @function\n */\nexport const fromValues = vec4.fromValues;\n\n/**\n * Copy the values from one quat to another\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the source quaternion\n * @returns {quat} out\n * @function\n */\nexport const copy = vec4.copy;\n\n/**\n * Set the components of a quat to the given values\n *\n * @param {quat} out the receiving quaternion\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @param {Number} w W component\n * @returns {quat} out\n * @function\n */\nexport const set = vec4.set;\n\n/**\n * Adds two quat's\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @returns {quat} out\n * @function\n */\nexport const add = vec4.add;\n\n/**\n * Alias for {@link quat.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Scales a quat by a scalar number\n *\n * @param {quat} out the receiving vector\n * @param {quat} a the vector to scale\n * @param {Number} b amount to scale the vector by\n * @returns {quat} out\n * @function\n */\nexport const scale = vec4.scale;\n\n/**\n * Calculates the dot product of two quat's\n *\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @returns {Number} dot product of a and b\n * @function\n */\nexport const dot = vec4.dot;\n\n/**\n * Performs a linear interpolation between two quat's\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {quat} out\n * @function\n */\nexport const lerp = vec4.lerp;\n\n/**\n * Calculates the length of a quat\n *\n * @param {quat} a vector to calculate length of\n * @returns {Number} length of a\n */\nexport const length = vec4.length;\n\n/**\n * Alias for {@link quat.length}\n * @function\n */\nexport const len = length;\n\n/**\n * Calculates the squared length of a quat\n *\n * @param {quat} a vector to calculate squared length of\n * @returns {Number} squared length of a\n * @function\n */\nexport const squaredLength = vec4.squaredLength;\n\n/**\n * Alias for {@link quat.squaredLength}\n * @function\n */\nexport const sqrLen = squaredLength;\n\n/**\n * Normalize a quat\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a quaternion to normalize\n * @returns {quat} out\n * @function\n */\nexport const normalize = vec4.normalize;\n\n/**\n * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===)\n *\n * @param {quat} a The first quaternion.\n * @param {quat} b The second quaternion.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport const exactEquals = vec4.exactEquals;\n\n/**\n * Returns whether or not the quaternions have approximately the same elements in the same position.\n *\n * @param {quat} a The first vector.\n * @param {quat} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport const equals = vec4.equals;\n\n/**\n * Sets a quaternion to represent the shortest rotation from one\n * vector to another.\n *\n * Both vectors are assumed to be unit length.\n *\n * @param {quat} out the receiving quaternion.\n * @param {vec3} a the initial vector\n * @param {vec3} b the destination vector\n * @returns {quat} out\n */\nexport const rotationTo = (function() {\n  let tmpvec3 = vec3.create();\n  let xUnitVec3 = vec3.fromValues(1,0,0);\n  let yUnitVec3 = vec3.fromValues(0,1,0);\n\n  return function(out, a, b) {\n    let dot = vec3.dot(a, b);\n    if (dot < -0.999999) {\n      vec3.cross(tmpvec3, xUnitVec3, a);\n      if (vec3.len(tmpvec3) < 0.000001)\n        vec3.cross(tmpvec3, yUnitVec3, a);\n      vec3.normalize(tmpvec3, tmpvec3);\n      setAxisAngle(out, tmpvec3, Math.PI);\n      return out;\n    } else if (dot > 0.999999) {\n      out[0] = 0;\n      out[1] = 0;\n      out[2] = 0;\n      out[3] = 1;\n      return out;\n    } else {\n      vec3.cross(tmpvec3, a, b);\n      out[0] = tmpvec3[0];\n      out[1] = tmpvec3[1];\n      out[2] = tmpvec3[2];\n      out[3] = 1 + dot;\n      return normalize(out, out);\n    }\n  };\n})();\n\n/**\n * Performs a spherical linear interpolation with two control points\n *\n * @param {quat} out the receiving quaternion\n * @param {quat} a the first operand\n * @param {quat} b the second operand\n * @param {quat} c the third operand\n * @param {quat} d the fourth operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {quat} out\n */\nexport const sqlerp = (function () {\n  let temp1 = create();\n  let temp2 = create();\n\n  return function (out, a, b, c, d, t) {\n    slerp(temp1, a, d, t);\n    slerp(temp2, b, c, t);\n    slerp(out, temp1, temp2, 2 * t * (1 - t));\n\n    return out;\n  };\n}());\n\n/**\n * Sets the specified quaternion with values corresponding to the given\n * axes. Each axis is a vec3 and is expected to be unit length and\n * perpendicular to all other specified axes.\n *\n * @param {vec3} view  the vector representing the viewing direction\n * @param {vec3} right the vector representing the local \"right\" direction\n * @param {vec3} up    the vector representing the local \"up\" direction\n * @returns {quat} out\n */\nexport const setAxes = (function() {\n  let matr = mat3.create();\n\n  return function(out, view, right, up) {\n    matr[0] = right[0];\n    matr[3] = right[1];\n    matr[6] = right[2];\n\n    matr[1] = up[0];\n    matr[4] = up[1];\n    matr[7] = up[2];\n\n    matr[2] = -view[0];\n    matr[5] = -view[1];\n    matr[8] = -view[2];\n\n    return normalize(out, fromMat3(out, matr));\n  };\n})();\n","import * as glMatrix from \"./common.js\";\nimport * as quat from \"./quat.js\";\nimport * as mat4 from \"./mat4.js\";\n\n/**\n * Dual Quaternion<br>\n * Format: [real, dual]<br>\n * Quaternion format: XYZW<br>\n * Make sure to have normalized dual quaternions, otherwise the functions may not work as intended.<br>\n * @module quat2\n */\n\n\n/**\n * Creates a new identity dual quat\n *\n * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation]\n */\nexport function create() {\n  let dq = new glMatrix.ARRAY_TYPE(8);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    dq[0] = 0;\n    dq[1] = 0;\n    dq[2] = 0;\n    dq[4] = 0;\n    dq[5] = 0;\n    dq[6] = 0;\n    dq[7] = 0;\n  }\n  dq[3] = 1;\n  return dq;\n}\n\n/**\n * Creates a new quat initialized with values from an existing quaternion\n *\n * @param {quat2} a dual quaternion to clone\n * @returns {quat2} new dual quaternion\n * @function\n */\nexport function clone(a) {\n  let dq = new glMatrix.ARRAY_TYPE(8);\n  dq[0] = a[0];\n  dq[1] = a[1];\n  dq[2] = a[2];\n  dq[3] = a[3];\n  dq[4] = a[4];\n  dq[5] = a[5];\n  dq[6] = a[6];\n  dq[7] = a[7];\n  return dq;\n}\n\n/**\n * Creates a new dual quat initialized with the given values\n *\n * @param {Number} x1 X component\n * @param {Number} y1 Y component\n * @param {Number} z1 Z component\n * @param {Number} w1 W component\n * @param {Number} x2 X component\n * @param {Number} y2 Y component\n * @param {Number} z2 Z component\n * @param {Number} w2 W component\n * @returns {quat2} new dual quaternion\n * @function\n */\nexport function fromValues(x1, y1, z1, w1, x2, y2, z2, w2) {\n  let dq = new glMatrix.ARRAY_TYPE(8);\n  dq[0] = x1;\n  dq[1] = y1;\n  dq[2] = z1;\n  dq[3] = w1;\n  dq[4] = x2;\n  dq[5] = y2;\n  dq[6] = z2;\n  dq[7] = w2;\n  return dq;\n}\n\n/**\n * Creates a new dual quat from the given values (quat and translation)\n *\n * @param {Number} x1 X component\n * @param {Number} y1 Y component\n * @param {Number} z1 Z component\n * @param {Number} w1 W component\n * @param {Number} x2 X component (translation)\n * @param {Number} y2 Y component (translation)\n * @param {Number} z2 Z component (translation)\n * @returns {quat2} new dual quaternion\n * @function\n */\nexport function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) {\n  let dq = new glMatrix.ARRAY_TYPE(8);\n  dq[0] = x1;\n  dq[1] = y1;\n  dq[2] = z1;\n  dq[3] = w1;\n  let ax = x2 * 0.5,\n    ay = y2 * 0.5,\n    az = z2 * 0.5;\n  dq[4] = ax * w1 + ay * z1 - az * y1;\n  dq[5] = ay * w1 + az * x1 - ax * z1;\n  dq[6] = az * w1 + ax * y1 - ay * x1;\n  dq[7] = -ax * x1 - ay * y1 - az * z1;\n  return dq;\n}\n\n/**\n * Creates a dual quat from a quaternion and a translation\n *\n * @param {quat2} dual quaternion receiving operation result\n * @param {quat} q quaternion\n * @param {vec3} t tranlation vector\n * @returns {quat2} dual quaternion receiving operation result\n * @function\n */\nexport function fromRotationTranslation(out, q, t) {\n  let ax = t[0] * 0.5,\n    ay = t[1] * 0.5,\n    az = t[2] * 0.5,\n    bx = q[0],\n    by = q[1],\n    bz = q[2],\n    bw = q[3];\n  out[0] = bx;\n  out[1] = by;\n  out[2] = bz;\n  out[3] = bw;\n  out[4] = ax * bw + ay * bz - az * by;\n  out[5] = ay * bw + az * bx - ax * bz;\n  out[6] = az * bw + ax * by - ay * bx;\n  out[7] = -ax * bx - ay * by - az * bz;\n  return out;\n}\n\n/**\n * Creates a dual quat from a translation\n *\n * @param {quat2} dual quaternion receiving operation result\n * @param {vec3} t translation vector\n * @returns {quat2} dual quaternion receiving operation result\n * @function\n */\nexport function fromTranslation(out, t) {\n  out[0] = 0;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  out[4] = t[0] * 0.5;\n  out[5] = t[1] * 0.5;\n  out[6] = t[2] * 0.5;\n  out[7] = 0;\n  return out;\n}\n\n/**\n * Creates a dual quat from a quaternion\n *\n * @param {quat2} dual quaternion receiving operation result\n * @param {quat} q the quaternion\n * @returns {quat2} dual quaternion receiving operation result\n * @function\n */\nexport function fromRotation(out, q) {\n  out[0] = q[0];\n  out[1] = q[1];\n  out[2] = q[2];\n  out[3] = q[3];\n  out[4] = 0;\n  out[5] = 0;\n  out[6] = 0;\n  out[7] = 0;\n  return out;\n}\n\n/**\n * Creates a new dual quat from a matrix (4x4)\n *\n * @param {quat2} out the dual quaternion\n * @param {mat4} a the matrix\n * @returns {quat2} dual quat receiving operation result\n * @function\n */\nexport function fromMat4(out, a) {\n  //TODO Optimize this\n  let outer = quat.create();\n  mat4.getRotation(outer, a);\n  let t = new glMatrix.ARRAY_TYPE(3);\n  mat4.getTranslation(t, a);\n  fromRotationTranslation(out, outer, t);\n  return out;\n}\n\n/**\n * Copy the values from one dual quat to another\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the source dual quaternion\n * @returns {quat2} out\n * @function\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  out[4] = a[4];\n  out[5] = a[5];\n  out[6] = a[6];\n  out[7] = a[7];\n  return out;\n}\n\n/**\n * Set a dual quat to the identity dual quaternion\n *\n * @param {quat2} out the receiving quaternion\n * @returns {quat2} out\n */\nexport function identity(out) {\n  out[0] = 0;\n  out[1] = 0;\n  out[2] = 0;\n  out[3] = 1;\n  out[4] = 0;\n  out[5] = 0;\n  out[6] = 0;\n  out[7] = 0;\n  return out;\n}\n\n/**\n * Set the components of a dual quat to the given values\n *\n * @param {quat2} out the receiving quaternion\n * @param {Number} x1 X component\n * @param {Number} y1 Y component\n * @param {Number} z1 Z component\n * @param {Number} w1 W component\n * @param {Number} x2 X component\n * @param {Number} y2 Y component\n * @param {Number} z2 Z component\n * @param {Number} w2 W component\n * @returns {quat2} out\n * @function\n */\nexport function set(out, x1, y1, z1, w1, x2, y2, z2, w2) {\n  out[0] = x1;\n  out[1] = y1;\n  out[2] = z1;\n  out[3] = w1;\n\n  out[4] = x2;\n  out[5] = y2;\n  out[6] = z2;\n  out[7] = w2;\n  return out;\n}\n\n/**\n * Gets the real part of a dual quat\n * @param  {quat} out real part\n * @param  {quat2} a Dual Quaternion\n * @return {quat} real part\n */\nexport const getReal = quat.copy;\n\n/**\n * Gets the dual part of a dual quat\n * @param  {quat} out dual part\n * @param  {quat2} a Dual Quaternion\n * @return {quat} dual part\n */\nexport function getDual(out, a) {\n  out[0] = a[4];\n  out[1] = a[5];\n  out[2] = a[6];\n  out[3] = a[7];\n  return out;\n}\n\n/**\n * Set the real component of a dual quat to the given quaternion\n *\n * @param {quat2} out the receiving quaternion\n * @param {quat} q a quaternion representing the real part\n * @returns {quat2} out\n * @function\n */\nexport const setReal = quat.copy;\n\n/**\n * Set the dual component of a dual quat to the given quaternion\n *\n * @param {quat2} out the receiving quaternion\n * @param {quat} q a quaternion representing the dual part\n * @returns {quat2} out\n * @function\n */\nexport function setDual(out, q) {\n  out[4] = q[0];\n  out[5] = q[1];\n  out[6] = q[2];\n  out[7] = q[3];\n  return out;\n}\n\n/**\n * Gets the translation of a normalized dual quat\n * @param  {vec3} out translation\n * @param  {quat2} a Dual Quaternion to be decomposed\n * @return {vec3} translation\n */\nexport function getTranslation(out, a) {\n  let ax = a[4],\n    ay = a[5],\n    az = a[6],\n    aw = a[7],\n    bx = -a[0],\n    by = -a[1],\n    bz = -a[2],\n    bw = a[3];\n  out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;\n  out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;\n  out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;\n  return out;\n}\n\n/**\n * Translates a dual quat by the given vector\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to translate\n * @param {vec3} v vector to translate by\n * @returns {quat2} out\n */\nexport function translate(out, a, v) {\n  let ax1 = a[0],\n    ay1 = a[1],\n    az1 = a[2],\n    aw1 = a[3],\n    bx1 = v[0] * 0.5,\n    by1 = v[1] * 0.5,\n    bz1 = v[2] * 0.5,\n    ax2 = a[4],\n    ay2 = a[5],\n    az2 = a[6],\n    aw2 = a[7];\n  out[0] = ax1;\n  out[1] = ay1;\n  out[2] = az1;\n  out[3] = aw1;\n  out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2;\n  out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2;\n  out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2;\n  out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2;\n  return out;\n}\n\n/**\n * Rotates a dual quat around the X axis\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to rotate\n * @param {number} rad how far should the rotation be\n * @returns {quat2} out\n */\nexport function rotateX(out, a, rad) {\n  let bx = -a[0],\n    by = -a[1],\n    bz = -a[2],\n    bw = a[3],\n    ax = a[4],\n    ay = a[5],\n    az = a[6],\n    aw = a[7],\n    ax1 = ax * bw + aw * bx + ay * bz - az * by,\n    ay1 = ay * bw + aw * by + az * bx - ax * bz,\n    az1 = az * bw + aw * bz + ax * by - ay * bx,\n    aw1 = aw * bw - ax * bx - ay * by - az * bz;\n  quat.rotateX(out, a, rad);\n  bx = out[0];\n  by = out[1];\n  bz = out[2];\n  bw = out[3];\n  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;\n  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;\n  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;\n  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;\n  return out;\n}\n\n/**\n * Rotates a dual quat around the Y axis\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to rotate\n * @param {number} rad how far should the rotation be\n * @returns {quat2} out\n */\nexport function rotateY(out, a, rad) {\n  let bx = -a[0],\n    by = -a[1],\n    bz = -a[2],\n    bw = a[3],\n    ax = a[4],\n    ay = a[5],\n    az = a[6],\n    aw = a[7],\n    ax1 = ax * bw + aw * bx + ay * bz - az * by,\n    ay1 = ay * bw + aw * by + az * bx - ax * bz,\n    az1 = az * bw + aw * bz + ax * by - ay * bx,\n    aw1 = aw * bw - ax * bx - ay * by - az * bz;\n  quat.rotateY(out, a, rad);\n  bx = out[0];\n  by = out[1];\n  bz = out[2];\n  bw = out[3];\n  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;\n  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;\n  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;\n  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;\n  return out;\n}\n\n/**\n * Rotates a dual quat around the Z axis\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to rotate\n * @param {number} rad how far should the rotation be\n * @returns {quat2} out\n */\nexport function rotateZ(out, a, rad) {\n  let bx = -a[0],\n    by = -a[1],\n    bz = -a[2],\n    bw = a[3],\n    ax = a[4],\n    ay = a[5],\n    az = a[6],\n    aw = a[7],\n    ax1 = ax * bw + aw * bx + ay * bz - az * by,\n    ay1 = ay * bw + aw * by + az * bx - ax * bz,\n    az1 = az * bw + aw * bz + ax * by - ay * bx,\n    aw1 = aw * bw - ax * bx - ay * by - az * bz;\n  quat.rotateZ(out, a, rad);\n  bx = out[0];\n  by = out[1];\n  bz = out[2];\n  bw = out[3];\n  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;\n  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;\n  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;\n  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;\n  return out;\n}\n\n/**\n * Rotates a dual quat by a given quaternion (a * q)\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to rotate\n * @param {quat} q quaternion to rotate by\n * @returns {quat2} out\n */\nexport function rotateByQuatAppend(out, a, q) {\n  let qx = q[0],\n    qy = q[1],\n    qz = q[2],\n    qw = q[3],\n    ax = a[0],\n    ay = a[1],\n    az = a[2],\n    aw = a[3];\n\n  out[0] = ax * qw + aw * qx + ay * qz - az * qy;\n  out[1] = ay * qw + aw * qy + az * qx - ax * qz;\n  out[2] = az * qw + aw * qz + ax * qy - ay * qx;\n  out[3] = aw * qw - ax * qx - ay * qy - az * qz;\n  ax = a[4];\n  ay = a[5];\n  az = a[6];\n  aw = a[7];\n  out[4] = ax * qw + aw * qx + ay * qz - az * qy;\n  out[5] = ay * qw + aw * qy + az * qx - ax * qz;\n  out[6] = az * qw + aw * qz + ax * qy - ay * qx;\n  out[7] = aw * qw - ax * qx - ay * qy - az * qz;\n  return out;\n}\n\n/**\n * Rotates a dual quat by a given quaternion (q * a)\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat} q quaternion to rotate by\n * @param {quat2} a the dual quaternion to rotate\n * @returns {quat2} out\n */\nexport function rotateByQuatPrepend(out, q, a) {\n  let qx = q[0],\n    qy = q[1],\n    qz = q[2],\n    qw = q[3],\n    bx = a[0],\n    by = a[1],\n    bz = a[2],\n    bw = a[3];\n\n  out[0] = qx * bw + qw * bx + qy * bz - qz * by;\n  out[1] = qy * bw + qw * by + qz * bx - qx * bz;\n  out[2] = qz * bw + qw * bz + qx * by - qy * bx;\n  out[3] = qw * bw - qx * bx - qy * by - qz * bz;\n  bx = a[4];\n  by = a[5];\n  bz = a[6];\n  bw = a[7];\n  out[4] = qx * bw + qw * bx + qy * bz - qz * by;\n  out[5] = qy * bw + qw * by + qz * bx - qx * bz;\n  out[6] = qz * bw + qw * bz + qx * by - qy * bx;\n  out[7] = qw * bw - qx * bx - qy * by - qz * bz;\n  return out;\n}\n\n/**\n * Rotates a dual quat around a given axis. Does the normalisation automatically\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the dual quaternion to rotate\n * @param {vec3} axis the axis to rotate around\n * @param {Number} rad how far the rotation should be\n * @returns {quat2} out\n */\nexport function rotateAroundAxis(out, a, axis, rad) {\n  //Special case for rad = 0\n  if (Math.abs(rad) < glMatrix.EPSILON) {\n    return copy(out, a);\n  }\n  let axisLength = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);\n\n  rad = rad * 0.5;\n  let s = Math.sin(rad);\n  let bx = s * axis[0] / axisLength;\n  let by = s * axis[1] / axisLength;\n  let bz = s * axis[2] / axisLength;\n  let bw = Math.cos(rad);\n\n  let ax1 = a[0],\n    ay1 = a[1],\n    az1 = a[2],\n    aw1 = a[3];\n  out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;\n  out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;\n  out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;\n  out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;\n\n  let ax = a[4],\n    ay = a[5],\n    az = a[6],\n    aw = a[7];\n  out[4] = ax * bw + aw * bx + ay * bz - az * by;\n  out[5] = ay * bw + aw * by + az * bx - ax * bz;\n  out[6] = az * bw + aw * bz + ax * by - ay * bx;\n  out[7] = aw * bw - ax * bx - ay * by - az * bz;\n\n  return out;\n}\n\n/**\n * Adds two dual quat's\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the first operand\n * @param {quat2} b the second operand\n * @returns {quat2} out\n * @function\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  out[4] = a[4] + b[4];\n  out[5] = a[5] + b[5];\n  out[6] = a[6] + b[6];\n  out[7] = a[7] + b[7];\n  return out;\n}\n\n/**\n * Multiplies two dual quat's\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a the first operand\n * @param {quat2} b the second operand\n * @returns {quat2} out\n */\nexport function multiply(out, a, b) {\n  let ax0 = a[0],\n    ay0 = a[1],\n    az0 = a[2],\n    aw0 = a[3],\n    bx1 = b[4],\n    by1 = b[5],\n    bz1 = b[6],\n    bw1 = b[7],\n    ax1 = a[4],\n    ay1 = a[5],\n    az1 = a[6],\n    aw1 = a[7],\n    bx0 = b[0],\n    by0 = b[1],\n    bz0 = b[2],\n    bw0 = b[3];\n  out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0;\n  out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0;\n  out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0;\n  out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0;\n  out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0;\n  out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0;\n  out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0;\n  out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0;\n  return out;\n}\n\n/**\n * Alias for {@link quat2.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Scales a dual quat by a scalar number\n *\n * @param {quat2} out the receiving dual quat\n * @param {quat2} a the dual quat to scale\n * @param {Number} b amount to scale the dual quat by\n * @returns {quat2} out\n * @function\n */\nexport function scale(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  out[4] = a[4] * b;\n  out[5] = a[5] * b;\n  out[6] = a[6] * b;\n  out[7] = a[7] * b;\n  return out;\n}\n\n/**\n * Calculates the dot product of two dual quat's (The dot product of the real parts)\n *\n * @param {quat2} a the first operand\n * @param {quat2} b the second operand\n * @returns {Number} dot product of a and b\n * @function\n */\nexport const dot = quat.dot;\n\n/**\n * Performs a linear interpolation between two dual quats's\n * NOTE: The resulting dual quaternions won't always be normalized (The error is most noticeable when t = 0.5)\n *\n * @param {quat2} out the receiving dual quat\n * @param {quat2} a the first operand\n * @param {quat2} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {quat2} out\n */\nexport function lerp(out, a, b, t) {\n  let mt = 1 - t;\n  if (dot(a, b) < 0) t = -t;\n\n  out[0] = a[0] * mt + b[0] * t;\n  out[1] = a[1] * mt + b[1] * t;\n  out[2] = a[2] * mt + b[2] * t;\n  out[3] = a[3] * mt + b[3] * t;\n  out[4] = a[4] * mt + b[4] * t;\n  out[5] = a[5] * mt + b[5] * t;\n  out[6] = a[6] * mt + b[6] * t;\n  out[7] = a[7] * mt + b[7] * t;\n\n  return out;\n}\n\n/**\n * Calculates the inverse of a dual quat. If they are normalized, conjugate is cheaper\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a dual quat to calculate inverse of\n * @returns {quat2} out\n */\nexport function invert(out, a) {\n  let sqlen = squaredLength(a);\n  out[0] = -a[0] / sqlen;\n  out[1] = -a[1] / sqlen;\n  out[2] = -a[2] / sqlen;\n  out[3] = a[3] / sqlen;\n  out[4] = -a[4] / sqlen;\n  out[5] = -a[5] / sqlen;\n  out[6] = -a[6] / sqlen;\n  out[7] = a[7] / sqlen;\n  return out;\n}\n\n/**\n * Calculates the conjugate of a dual quat\n * If the dual quaternion is normalized, this function is faster than quat2.inverse and produces the same result.\n *\n * @param {quat2} out the receiving quaternion\n * @param {quat2} a quat to calculate conjugate of\n * @returns {quat2} out\n */\nexport function conjugate(out, a) {\n  out[0] = -a[0];\n  out[1] = -a[1];\n  out[2] = -a[2];\n  out[3] = a[3];\n  out[4] = -a[4];\n  out[5] = -a[5];\n  out[6] = -a[6];\n  out[7] = a[7];\n  return out;\n}\n\n/**\n * Calculates the length of a dual quat\n *\n * @param {quat2} a dual quat to calculate length of\n * @returns {Number} length of a\n * @function\n */\nexport const length = quat.length;\n\n/**\n * Alias for {@link quat2.length}\n * @function\n */\nexport const len = length;\n\n/**\n * Calculates the squared length of a dual quat\n *\n * @param {quat2} a dual quat to calculate squared length of\n * @returns {Number} squared length of a\n * @function\n */\nexport const squaredLength = quat.squaredLength;\n\n/**\n * Alias for {@link quat2.squaredLength}\n * @function\n */\nexport const sqrLen = squaredLength;\n\n/**\n * Normalize a dual quat\n *\n * @param {quat2} out the receiving dual quaternion\n * @param {quat2} a dual quaternion to normalize\n * @returns {quat2} out\n * @function\n */\nexport function normalize(out, a) {\n  let magnitude = squaredLength(a);\n  if (magnitude > 0) {\n    magnitude = Math.sqrt(magnitude);\n\n    let a0 = a[0] / magnitude;\n    let a1 = a[1] / magnitude;\n    let a2 = a[2] / magnitude;\n    let a3 = a[3] / magnitude;\n\n    let b0 = a[4];\n    let b1 = a[5];\n    let b2 = a[6];\n    let b3 = a[7];\n\n    let a_dot_b = (a0 * b0) + (a1 * b1) + (a2 * b2) + (a3 * b3);\n\n    out[0] = a0;\n    out[1] = a1;\n    out[2] = a2;\n    out[3] = a3;\n\n    out[4] = (b0 - (a0 * a_dot_b)) / magnitude;\n    out[5] = (b1 - (a1 * a_dot_b)) / magnitude;\n    out[6] = (b2 - (a2 * a_dot_b)) / magnitude;\n    out[7] = (b3 - (a3 * a_dot_b)) / magnitude;\n  }\n  return out;\n}\n\n/**\n * Returns a string representation of a dual quatenion\n *\n * @param {quat2} a dual quaternion to represent as a string\n * @returns {String} string representation of the dual quat\n */\nexport function str(a) {\n  return 'quat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +\n    a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ')';\n}\n\n/**\n * Returns whether or not the dual quaternions have exactly the same elements in the same position (when compared with ===)\n *\n * @param {quat2} a the first dual quaternion.\n * @param {quat2} b the second dual quaternion.\n * @returns {Boolean} true if the dual quaternions are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&\n    a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7];\n}\n\n/**\n * Returns whether or not the dual quaternions have approximately the same elements in the same position.\n *\n * @param {quat2} a the first dual quat.\n * @param {quat2} b the second dual quat.\n * @returns {Boolean} true if the dual quats are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0],\n    a1 = a[1],\n    a2 = a[2],\n    a3 = a[3],\n    a4 = a[4],\n    a5 = a[5],\n    a6 = a[6],\n    a7 = a[7];\n  let b0 = b[0],\n    b1 = b[1],\n    b2 = b[2],\n    b3 = b[3],\n    b4 = b[4],\n    b5 = b[5],\n    b6 = b[6],\n    b7 = b[7];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n    Math.abs(a1 - b1) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n    Math.abs(a2 - b2) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n    Math.abs(a3 - b3) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&\n    Math.abs(a4 - b4) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&\n    Math.abs(a5 - b5) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&\n    Math.abs(a6 - b6) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&\n    Math.abs(a7 - b7) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)));\n}\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 2 Dimensional Vector\n * @module vec2\n */\n\n/**\n * Creates a new, empty vec2\n *\n * @returns {vec2} a new 2D vector\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(2);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[0] = 0;\n    out[1] = 0;\n  }\n  return out;\n}\n\n/**\n * Creates a new vec2 initialized with values from an existing vector\n *\n * @param {vec2} a vector to clone\n * @returns {vec2} a new 2D vector\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(2);\n  out[0] = a[0];\n  out[1] = a[1];\n  return out;\n}\n\n/**\n * Creates a new vec2 initialized with the given values\n *\n * @param {Number} x X component\n * @param {Number} y Y component\n * @returns {vec2} a new 2D vector\n */\nexport function fromValues(x, y) {\n  let out = new glMatrix.ARRAY_TYPE(2);\n  out[0] = x;\n  out[1] = y;\n  return out;\n}\n\n/**\n * Copy the values from one vec2 to another\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the source vector\n * @returns {vec2} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  return out;\n}\n\n/**\n * Set the components of a vec2 to the given values\n *\n * @param {vec2} out the receiving vector\n * @param {Number} x X component\n * @param {Number} y Y component\n * @returns {vec2} out\n */\nexport function set(out, x, y) {\n  out[0] = x;\n  out[1] = y;\n  return out;\n}\n\n/**\n * Adds two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  return out;\n}\n\n/**\n * Subtracts vector b from vector a\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  return out;\n}\n\n/**\n * Multiplies two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function multiply(out, a, b) {\n  out[0] = a[0] * b[0];\n  out[1] = a[1] * b[1];\n  return out;\n}\n\n/**\n * Divides two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function divide(out, a, b) {\n  out[0] = a[0] / b[0];\n  out[1] = a[1] / b[1];\n  return out;\n}\n\n/**\n * Math.ceil the components of a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to ceil\n * @returns {vec2} out\n */\nexport function ceil(out, a) {\n  out[0] = Math.ceil(a[0]);\n  out[1] = Math.ceil(a[1]);\n  return out;\n}\n\n/**\n * Math.floor the components of a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to floor\n * @returns {vec2} out\n */\nexport function floor(out, a) {\n  out[0] = Math.floor(a[0]);\n  out[1] = Math.floor(a[1]);\n  return out;\n}\n\n/**\n * Returns the minimum of two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function min(out, a, b) {\n  out[0] = Math.min(a[0], b[0]);\n  out[1] = Math.min(a[1], b[1]);\n  return out;\n}\n\n/**\n * Returns the maximum of two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec2} out\n */\nexport function max(out, a, b) {\n  out[0] = Math.max(a[0], b[0]);\n  out[1] = Math.max(a[1], b[1]);\n  return out;\n}\n\n/**\n * Math.round the components of a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to round\n * @returns {vec2} out\n */\nexport function round (out, a) {\n  out[0] = Math.round(a[0]);\n  out[1] = Math.round(a[1]);\n  return out;\n}\n\n/**\n * Scales a vec2 by a scalar number\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the vector to scale\n * @param {Number} b amount to scale the vector by\n * @returns {vec2} out\n */\nexport function scale(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  return out;\n}\n\n/**\n * Adds two vec2's after scaling the second operand by a scalar value\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @param {Number} scale the amount to scale b by before adding\n * @returns {vec2} out\n */\nexport function scaleAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  return out;\n}\n\n/**\n * Calculates the euclidian distance between two vec2's\n *\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {Number} distance between a and b\n */\nexport function distance(a, b) {\n  var x = b[0] - a[0],\n    y = b[1] - a[1];\n  return Math.sqrt(x*x + y*y);\n}\n\n/**\n * Calculates the squared euclidian distance between two vec2's\n *\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {Number} squared distance between a and b\n */\nexport function squaredDistance(a, b) {\n  var x = b[0] - a[0],\n    y = b[1] - a[1];\n  return x*x + y*y;\n}\n\n/**\n * Calculates the length of a vec2\n *\n * @param {vec2} a vector to calculate length of\n * @returns {Number} length of a\n */\nexport function length(a) {\n  var x = a[0],\n    y = a[1];\n  return Math.sqrt(x*x + y*y);\n}\n\n/**\n * Calculates the squared length of a vec2\n *\n * @param {vec2} a vector to calculate squared length of\n * @returns {Number} squared length of a\n */\nexport function squaredLength (a) {\n  var x = a[0],\n    y = a[1];\n  return x*x + y*y;\n}\n\n/**\n * Negates the components of a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to negate\n * @returns {vec2} out\n */\nexport function negate(out, a) {\n  out[0] = -a[0];\n  out[1] = -a[1];\n  return out;\n}\n\n/**\n * Returns the inverse of the components of a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to invert\n * @returns {vec2} out\n */\nexport function inverse(out, a) {\n  out[0] = 1.0 / a[0];\n  out[1] = 1.0 / a[1];\n  return out;\n}\n\n/**\n * Normalize a vec2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a vector to normalize\n * @returns {vec2} out\n */\nexport function normalize(out, a) {\n  var x = a[0],\n    y = a[1];\n  var len = x*x + y*y;\n  if (len > 0) {\n    //TODO: evaluate use of glm_invsqrt here?\n    len = 1 / Math.sqrt(len);\n    out[0] = a[0] * len;\n    out[1] = a[1] * len;\n  }\n  return out;\n}\n\n/**\n * Calculates the dot product of two vec2's\n *\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {Number} dot product of a and b\n */\nexport function dot(a, b) {\n  return a[0] * b[0] + a[1] * b[1];\n}\n\n/**\n * Computes the cross product of two vec2's\n * Note that the cross product must by definition produce a 3D vector\n *\n * @param {vec3} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @returns {vec3} out\n */\nexport function cross(out, a, b) {\n  var z = a[0] * b[1] - a[1] * b[0];\n  out[0] = out[1] = 0;\n  out[2] = z;\n  return out;\n}\n\n/**\n * Performs a linear interpolation between two vec2's\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the first operand\n * @param {vec2} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec2} out\n */\nexport function lerp(out, a, b, t) {\n  var ax = a[0],\n    ay = a[1];\n  out[0] = ax + t * (b[0] - ax);\n  out[1] = ay + t * (b[1] - ay);\n  return out;\n}\n\n/**\n * Generates a random vector with the given scale\n *\n * @param {vec2} out the receiving vector\n * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned\n * @returns {vec2} out\n */\nexport function random(out, scale) {\n  scale = scale || 1.0;\n  var r = glMatrix.RANDOM() * 2.0 * Math.PI;\n  out[0] = Math.cos(r) * scale;\n  out[1] = Math.sin(r) * scale;\n  return out;\n}\n\n/**\n * Transforms the vec2 with a mat2\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the vector to transform\n * @param {mat2} m matrix to transform with\n * @returns {vec2} out\n */\nexport function transformMat2(out, a, m) {\n  var x = a[0],\n    y = a[1];\n  out[0] = m[0] * x + m[2] * y;\n  out[1] = m[1] * x + m[3] * y;\n  return out;\n}\n\n/**\n * Transforms the vec2 with a mat2d\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the vector to transform\n * @param {mat2d} m matrix to transform with\n * @returns {vec2} out\n */\nexport function transformMat2d(out, a, m) {\n  var x = a[0],\n    y = a[1];\n  out[0] = m[0] * x + m[2] * y + m[4];\n  out[1] = m[1] * x + m[3] * y + m[5];\n  return out;\n}\n\n/**\n * Transforms the vec2 with a mat3\n * 3rd vector component is implicitly '1'\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the vector to transform\n * @param {mat3} m matrix to transform with\n * @returns {vec2} out\n */\nexport function transformMat3(out, a, m) {\n  var x = a[0],\n    y = a[1];\n  out[0] = m[0] * x + m[3] * y + m[6];\n  out[1] = m[1] * x + m[4] * y + m[7];\n  return out;\n}\n\n/**\n * Transforms the vec2 with a mat4\n * 3rd vector component is implicitly '0'\n * 4th vector component is implicitly '1'\n *\n * @param {vec2} out the receiving vector\n * @param {vec2} a the vector to transform\n * @param {mat4} m matrix to transform with\n * @returns {vec2} out\n */\nexport function transformMat4(out, a, m) {\n  let x = a[0];\n  let y = a[1];\n  out[0] = m[0] * x + m[4] * y + m[12];\n  out[1] = m[1] * x + m[5] * y + m[13];\n  return out;\n}\n\n/**\n * Rotate a 2D vector\n * @param {vec2} out The receiving vec2\n * @param {vec2} a The vec2 point to rotate\n * @param {vec2} b The origin of the rotation\n * @param {Number} c The angle of rotation\n * @returns {vec2} out\n */\nexport function rotate(out, a, b, c) {\n  //Translate point to the origin\n  let p0 = a[0] - b[0],\n  p1 = a[1] - b[1],\n  sinC = Math.sin(c),\n  cosC = Math.cos(c);\n  \n  //perform rotation and translate to correct position\n  out[0] = p0*cosC - p1*sinC + b[0];\n  out[1] = p0*sinC + p1*cosC + b[1];\n\n  return out;\n}\n\n/**\n * Get the angle between two 2D vectors\n * @param {vec2} a The first operand\n * @param {vec2} b The second operand\n * @returns {Number} The angle in radians\n */\nexport function angle(a, b) {\n  let x1 = a[0],\n    y1 = a[1],\n    x2 = b[0],\n    y2 = b[1];\n  \n  let len1 = x1*x1 + y1*y1;\n  if (len1 > 0) {\n    //TODO: evaluate use of glm_invsqrt here?\n    len1 = 1 / Math.sqrt(len1);\n  }\n  \n  let len2 = x2*x2 + y2*y2;\n  if (len2 > 0) {\n    //TODO: evaluate use of glm_invsqrt here?\n    len2 = 1 / Math.sqrt(len2);\n  }\n  \n  let cosine = (x1 * x2 + y1 * y2) * len1 * len2;\n  \n  \n  if(cosine > 1.0) {\n    return 0;\n  }\n  else if(cosine < -1.0) {\n    return Math.PI;\n  } else {\n    return Math.acos(cosine);\n  }\n}\n\n/**\n * Returns a string representation of a vector\n *\n * @param {vec2} a vector to represent as a string\n * @returns {String} string representation of the vector\n */\nexport function str(a) {\n  return 'vec2(' + a[0] + ', ' + a[1] + ')';\n}\n\n/**\n * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===)\n *\n * @param {vec2} a The first vector.\n * @param {vec2} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1];\n}\n\n/**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n *\n * @param {vec2} a The first vector.\n * @param {vec2} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1];\n  let b0 = b[0], b1 = b[1];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)));\n}\n\n/**\n * Alias for {@link vec2.length}\n * @function\n */\nexport const len = length;\n\n/**\n * Alias for {@link vec2.subtract}\n * @function\n */\nexport const sub = subtract;\n\n/**\n * Alias for {@link vec2.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link vec2.divide}\n * @function\n */\nexport const div = divide;\n\n/**\n * Alias for {@link vec2.distance}\n * @function\n */\nexport const dist = distance;\n\n/**\n * Alias for {@link vec2.squaredDistance}\n * @function\n */\nexport const sqrDist = squaredDistance;\n\n/**\n * Alias for {@link vec2.squaredLength}\n * @function\n */\nexport const sqrLen = squaredLength;\n\n/**\n * Perform some operation over an array of vec2s.\n *\n * @param {Array} a the array of vectors to iterate over\n * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed\n * @param {Number} offset Number of elements to skip at the beginning of the array\n * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array\n * @param {Function} fn Function to call for each vector in the array\n * @param {Object} [arg] additional argument to pass to fn\n * @returns {Array} a\n * @function\n */\nexport const forEach = (function() {\n  let vec = create();\n\n  return function(a, stride, offset, count, fn, arg) {\n    let i, l;\n    if(!stride) {\n      stride = 2;\n    }\n\n    if(!offset) {\n      offset = 0;\n    }\n\n    if(count) {\n      l = Math.min((count * stride) + offset, a.length);\n    } else {\n      l = a.length;\n    }\n\n    for(i = offset; i < l; i += stride) {\n      vec[0] = a[i]; vec[1] = a[i+1];\n      fn(vec, vec, arg);\n      a[i] = vec[0]; a[i+1] = vec[1];\n    }\n\n    return a;\n  };\n})();\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 3 Dimensional Vector\n * @module vec3\n */\n\n/**\n * Creates a new, empty vec3\n *\n * @returns {vec3} a new 3D vector\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(3);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[0] = 0;\n    out[1] = 0;\n    out[2] = 0;\n  }\n  return out;\n}\n\n/**\n * Creates a new vec3 initialized with values from an existing vector\n *\n * @param {vec3} a vector to clone\n * @returns {vec3} a new 3D vector\n */\nexport function clone(a) {\n  var out = new glMatrix.ARRAY_TYPE(3);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  return out;\n}\n\n/**\n * Calculates the length of a vec3\n *\n * @param {vec3} a vector to calculate length of\n * @returns {Number} length of a\n */\nexport function length(a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  return Math.sqrt(x*x + y*y + z*z);\n}\n\n/**\n * Creates a new vec3 initialized with the given values\n *\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @returns {vec3} a new 3D vector\n */\nexport function fromValues(x, y, z) {\n  let out = new glMatrix.ARRAY_TYPE(3);\n  out[0] = x;\n  out[1] = y;\n  out[2] = z;\n  return out;\n}\n\n/**\n * Copy the values from one vec3 to another\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the source vector\n * @returns {vec3} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  return out;\n}\n\n/**\n * Set the components of a vec3 to the given values\n *\n * @param {vec3} out the receiving vector\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @returns {vec3} out\n */\nexport function set(out, x, y, z) {\n  out[0] = x;\n  out[1] = y;\n  out[2] = z;\n  return out;\n}\n\n/**\n * Adds two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  return out;\n}\n\n/**\n * Subtracts vector b from vector a\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  return out;\n}\n\n/**\n * Multiplies two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function multiply(out, a, b) {\n  out[0] = a[0] * b[0];\n  out[1] = a[1] * b[1];\n  out[2] = a[2] * b[2];\n  return out;\n}\n\n/**\n * Divides two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function divide(out, a, b) {\n  out[0] = a[0] / b[0];\n  out[1] = a[1] / b[1];\n  out[2] = a[2] / b[2];\n  return out;\n}\n\n/**\n * Math.ceil the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to ceil\n * @returns {vec3} out\n */\nexport function ceil(out, a) {\n  out[0] = Math.ceil(a[0]);\n  out[1] = Math.ceil(a[1]);\n  out[2] = Math.ceil(a[2]);\n  return out;\n}\n\n/**\n * Math.floor the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to floor\n * @returns {vec3} out\n */\nexport function floor(out, a) {\n  out[0] = Math.floor(a[0]);\n  out[1] = Math.floor(a[1]);\n  out[2] = Math.floor(a[2]);\n  return out;\n}\n\n/**\n * Returns the minimum of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function min(out, a, b) {\n  out[0] = Math.min(a[0], b[0]);\n  out[1] = Math.min(a[1], b[1]);\n  out[2] = Math.min(a[2], b[2]);\n  return out;\n}\n\n/**\n * Returns the maximum of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function max(out, a, b) {\n  out[0] = Math.max(a[0], b[0]);\n  out[1] = Math.max(a[1], b[1]);\n  out[2] = Math.max(a[2], b[2]);\n  return out;\n}\n\n/**\n * Math.round the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to round\n * @returns {vec3} out\n */\nexport function round(out, a) {\n  out[0] = Math.round(a[0]);\n  out[1] = Math.round(a[1]);\n  out[2] = Math.round(a[2]);\n  return out;\n}\n\n/**\n * Scales a vec3 by a scalar number\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the vector to scale\n * @param {Number} b amount to scale the vector by\n * @returns {vec3} out\n */\nexport function scale(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  return out;\n}\n\n/**\n * Adds two vec3's after scaling the second operand by a scalar value\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @param {Number} scale the amount to scale b by before adding\n * @returns {vec3} out\n */\nexport function scaleAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  return out;\n}\n\n/**\n * Calculates the euclidian distance between two vec3's\n *\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {Number} distance between a and b\n */\nexport function distance(a, b) {\n  let x = b[0] - a[0];\n  let y = b[1] - a[1];\n  let z = b[2] - a[2];\n  return Math.sqrt(x*x + y*y + z*z);\n}\n\n/**\n * Calculates the squared euclidian distance between two vec3's\n *\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {Number} squared distance between a and b\n */\nexport function squaredDistance(a, b) {\n  let x = b[0] - a[0];\n  let y = b[1] - a[1];\n  let z = b[2] - a[2];\n  return x*x + y*y + z*z;\n}\n\n/**\n * Calculates the squared length of a vec3\n *\n * @param {vec3} a vector to calculate squared length of\n * @returns {Number} squared length of a\n */\nexport function squaredLength(a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  return x*x + y*y + z*z;\n}\n\n/**\n * Negates the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to negate\n * @returns {vec3} out\n */\nexport function negate(out, a) {\n  out[0] = -a[0];\n  out[1] = -a[1];\n  out[2] = -a[2];\n  return out;\n}\n\n/**\n * Returns the inverse of the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to invert\n * @returns {vec3} out\n */\nexport function inverse(out, a) {\n  out[0] = 1.0 / a[0];\n  out[1] = 1.0 / a[1];\n  out[2] = 1.0 / a[2];\n  return out;\n}\n\n/**\n * Normalize a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a vector to normalize\n * @returns {vec3} out\n */\nexport function normalize(out, a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  let len = x*x + y*y + z*z;\n  if (len > 0) {\n    //TODO: evaluate use of glm_invsqrt here?\n    len = 1 / Math.sqrt(len);\n    out[0] = a[0] * len;\n    out[1] = a[1] * len;\n    out[2] = a[2] * len;\n  }\n  return out;\n}\n\n/**\n * Calculates the dot product of two vec3's\n *\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {Number} dot product of a and b\n */\nexport function dot(a, b) {\n  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n\n/**\n * Computes the cross product of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @returns {vec3} out\n */\nexport function cross(out, a, b) {\n  let ax = a[0], ay = a[1], az = a[2];\n  let bx = b[0], by = b[1], bz = b[2];\n\n  out[0] = ay * bz - az * by;\n  out[1] = az * bx - ax * bz;\n  out[2] = ax * by - ay * bx;\n  return out;\n}\n\n/**\n * Performs a linear interpolation between two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\nexport function lerp(out, a, b, t) {\n  let ax = a[0];\n  let ay = a[1];\n  let az = a[2];\n  out[0] = ax + t * (b[0] - ax);\n  out[1] = ay + t * (b[1] - ay);\n  out[2] = az + t * (b[2] - az);\n  return out;\n}\n\n/**\n * Performs a hermite interpolation with two control points\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @param {vec3} c the third operand\n * @param {vec3} d the fourth operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\nexport function hermite(out, a, b, c, d, t) {\n  let factorTimes2 = t * t;\n  let factor1 = factorTimes2 * (2 * t - 3) + 1;\n  let factor2 = factorTimes2 * (t - 2) + t;\n  let factor3 = factorTimes2 * (t - 1);\n  let factor4 = factorTimes2 * (3 - 2 * t);\n\n  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n\n  return out;\n}\n\n/**\n * Performs a bezier interpolation with two control points\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the first operand\n * @param {vec3} b the second operand\n * @param {vec3} c the third operand\n * @param {vec3} d the fourth operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\nexport function bezier(out, a, b, c, d, t) {\n  let inverseFactor = 1 - t;\n  let inverseFactorTimesTwo = inverseFactor * inverseFactor;\n  let factorTimes2 = t * t;\n  let factor1 = inverseFactorTimesTwo * inverseFactor;\n  let factor2 = 3 * t * inverseFactorTimesTwo;\n  let factor3 = 3 * factorTimes2 * inverseFactor;\n  let factor4 = factorTimes2 * t;\n\n  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n\n  return out;\n}\n\n/**\n * Generates a random vector with the given scale\n *\n * @param {vec3} out the receiving vector\n * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned\n * @returns {vec3} out\n */\nexport function random(out, scale) {\n  scale = scale || 1.0;\n\n  let r = glMatrix.RANDOM() * 2.0 * Math.PI;\n  let z = (glMatrix.RANDOM() * 2.0) - 1.0;\n  let zScale = Math.sqrt(1.0-z*z) * scale;\n\n  out[0] = Math.cos(r) * zScale;\n  out[1] = Math.sin(r) * zScale;\n  out[2] = z * scale;\n  return out;\n}\n\n/**\n * Transforms the vec3 with a mat4.\n * 4th vector component is implicitly '1'\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the vector to transform\n * @param {mat4} m matrix to transform with\n * @returns {vec3} out\n */\nexport function transformMat4(out, a, m) {\n  let x = a[0], y = a[1], z = a[2];\n  let w = m[3] * x + m[7] * y + m[11] * z + m[15];\n  w = w || 1.0;\n  out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;\n  out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;\n  out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;\n  return out;\n}\n\n/**\n * Transforms the vec3 with a mat3.\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the vector to transform\n * @param {mat3} m the 3x3 matrix to transform with\n * @returns {vec3} out\n */\nexport function transformMat3(out, a, m) {\n  let x = a[0], y = a[1], z = a[2];\n  out[0] = x * m[0] + y * m[3] + z * m[6];\n  out[1] = x * m[1] + y * m[4] + z * m[7];\n  out[2] = x * m[2] + y * m[5] + z * m[8];\n  return out;\n}\n\n/**\n * Transforms the vec3 with a quat\n * Can also be used for dual quaternions. (Multiply it with the real part)\n *\n * @param {vec3} out the receiving vector\n * @param {vec3} a the vector to transform\n * @param {quat} q quaternion to transform with\n * @returns {vec3} out\n */\nexport function transformQuat(out, a, q) {\n    // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed\n    let qx = q[0], qy = q[1], qz = q[2], qw = q[3];\n    let x = a[0], y = a[1], z = a[2];\n    // var qvec = [qx, qy, qz];\n    // var uv = vec3.cross([], qvec, a);\n    let uvx = qy * z - qz * y,\n        uvy = qz * x - qx * z,\n        uvz = qx * y - qy * x;\n    // var uuv = vec3.cross([], qvec, uv);\n    let uuvx = qy * uvz - qz * uvy,\n        uuvy = qz * uvx - qx * uvz,\n        uuvz = qx * uvy - qy * uvx;\n    // vec3.scale(uv, uv, 2 * w);\n    let w2 = qw * 2;\n    uvx *= w2;\n    uvy *= w2;\n    uvz *= w2;\n    // vec3.scale(uuv, uuv, 2);\n    uuvx *= 2;\n    uuvy *= 2;\n    uuvz *= 2;\n    // return vec3.add(out, a, vec3.add(out, uv, uuv));\n    out[0] = x + uvx + uuvx;\n    out[1] = y + uvy + uuvy;\n    out[2] = z + uvz + uuvz;\n    return out;\n}\n\n/**\n * Rotate a 3D vector around the x-axis\n * @param {vec3} out The receiving vec3\n * @param {vec3} a The vec3 point to rotate\n * @param {vec3} b The origin of the rotation\n * @param {Number} c The angle of rotation\n * @returns {vec3} out\n */\nexport function rotateX(out, a, b, c){\n  let p = [], r=[];\n  //Translate point to the origin\n  p[0] = a[0] - b[0];\n  p[1] = a[1] - b[1];\n  p[2] = a[2] - b[2];\n\n  //perform rotation\n  r[0] = p[0];\n  r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c);\n  r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c);\n\n  //translate to correct position\n  out[0] = r[0] + b[0];\n  out[1] = r[1] + b[1];\n  out[2] = r[2] + b[2];\n\n  return out;\n}\n\n/**\n * Rotate a 3D vector around the y-axis\n * @param {vec3} out The receiving vec3\n * @param {vec3} a The vec3 point to rotate\n * @param {vec3} b The origin of the rotation\n * @param {Number} c The angle of rotation\n * @returns {vec3} out\n */\nexport function rotateY(out, a, b, c){\n  let p = [], r=[];\n  //Translate point to the origin\n  p[0] = a[0] - b[0];\n  p[1] = a[1] - b[1];\n  p[2] = a[2] - b[2];\n\n  //perform rotation\n  r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c);\n  r[1] = p[1];\n  r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c);\n\n  //translate to correct position\n  out[0] = r[0] + b[0];\n  out[1] = r[1] + b[1];\n  out[2] = r[2] + b[2];\n\n  return out;\n}\n\n/**\n * Rotate a 3D vector around the z-axis\n * @param {vec3} out The receiving vec3\n * @param {vec3} a The vec3 point to rotate\n * @param {vec3} b The origin of the rotation\n * @param {Number} c The angle of rotation\n * @returns {vec3} out\n */\nexport function rotateZ(out, a, b, c){\n  let p = [], r=[];\n  //Translate point to the origin\n  p[0] = a[0] - b[0];\n  p[1] = a[1] - b[1];\n  p[2] = a[2] - b[2];\n\n  //perform rotation\n  r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c);\n  r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c);\n  r[2] = p[2];\n\n  //translate to correct position\n  out[0] = r[0] + b[0];\n  out[1] = r[1] + b[1];\n  out[2] = r[2] + b[2];\n\n  return out;\n}\n\n/**\n * Get the angle between two 3D vectors\n * @param {vec3} a The first operand\n * @param {vec3} b The second operand\n * @returns {Number} The angle in radians\n */\nexport function angle(a, b) {\n  let tempA = fromValues(a[0], a[1], a[2]);\n  let tempB = fromValues(b[0], b[1], b[2]);\n\n  normalize(tempA, tempA);\n  normalize(tempB, tempB);\n\n  let cosine = dot(tempA, tempB);\n\n  if(cosine > 1.0) {\n    return 0;\n  }\n  else if(cosine < -1.0) {\n    return Math.PI;\n  } else {\n    return Math.acos(cosine);\n  }\n}\n\n/**\n * Returns a string representation of a vector\n *\n * @param {vec3} a vector to represent as a string\n * @returns {String} string representation of the vector\n */\nexport function str(a) {\n  return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')';\n}\n\n/**\n * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)\n *\n * @param {vec3} a The first vector.\n * @param {vec3} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n\n/**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n *\n * @param {vec3} a The first vector.\n * @param {vec3} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2];\n  let b0 = b[0], b1 = b[1], b2 = b[2];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)));\n}\n\n/**\n * Alias for {@link vec3.subtract}\n * @function\n */\nexport const sub = subtract;\n\n/**\n * Alias for {@link vec3.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link vec3.divide}\n * @function\n */\nexport const div = divide;\n\n/**\n * Alias for {@link vec3.distance}\n * @function\n */\nexport const dist = distance;\n\n/**\n * Alias for {@link vec3.squaredDistance}\n * @function\n */\nexport const sqrDist = squaredDistance;\n\n/**\n * Alias for {@link vec3.length}\n * @function\n */\nexport const len = length;\n\n/**\n * Alias for {@link vec3.squaredLength}\n * @function\n */\nexport const sqrLen = squaredLength;\n\n/**\n * Perform some operation over an array of vec3s.\n *\n * @param {Array} a the array of vectors to iterate over\n * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed\n * @param {Number} offset Number of elements to skip at the beginning of the array\n * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array\n * @param {Function} fn Function to call for each vector in the array\n * @param {Object} [arg] additional argument to pass to fn\n * @returns {Array} a\n * @function\n */\nexport const forEach = (function() {\n  let vec = create();\n\n  return function(a, stride, offset, count, fn, arg) {\n    let i, l;\n    if(!stride) {\n      stride = 3;\n    }\n\n    if(!offset) {\n      offset = 0;\n    }\n\n    if(count) {\n      l = Math.min((count * stride) + offset, a.length);\n    } else {\n      l = a.length;\n    }\n\n    for(i = offset; i < l; i += stride) {\n      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2];\n      fn(vec, vec, arg);\n      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2];\n    }\n\n    return a;\n  };\n})();\n","import * as glMatrix from \"./common.js\";\n\n/**\n * 4 Dimensional Vector\n * @module vec4\n */\n\n/**\n * Creates a new, empty vec4\n *\n * @returns {vec4} a new 4D vector\n */\nexport function create() {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  if(glMatrix.ARRAY_TYPE != Float32Array) {\n    out[0] = 0;\n    out[1] = 0;\n    out[2] = 0;\n    out[3] = 0;\n  }\n  return out;\n}\n\n/**\n * Creates a new vec4 initialized with values from an existing vector\n *\n * @param {vec4} a vector to clone\n * @returns {vec4} a new 4D vector\n */\nexport function clone(a) {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Creates a new vec4 initialized with the given values\n *\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @param {Number} w W component\n * @returns {vec4} a new 4D vector\n */\nexport function fromValues(x, y, z, w) {\n  let out = new glMatrix.ARRAY_TYPE(4);\n  out[0] = x;\n  out[1] = y;\n  out[2] = z;\n  out[3] = w;\n  return out;\n}\n\n/**\n * Copy the values from one vec4 to another\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the source vector\n * @returns {vec4} out\n */\nexport function copy(out, a) {\n  out[0] = a[0];\n  out[1] = a[1];\n  out[2] = a[2];\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Set the components of a vec4 to the given values\n *\n * @param {vec4} out the receiving vector\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @param {Number} w W component\n * @returns {vec4} out\n */\nexport function set(out, x, y, z, w) {\n  out[0] = x;\n  out[1] = y;\n  out[2] = z;\n  out[3] = w;\n  return out;\n}\n\n/**\n * Adds two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function add(out, a, b) {\n  out[0] = a[0] + b[0];\n  out[1] = a[1] + b[1];\n  out[2] = a[2] + b[2];\n  out[3] = a[3] + b[3];\n  return out;\n}\n\n/**\n * Subtracts vector b from vector a\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function subtract(out, a, b) {\n  out[0] = a[0] - b[0];\n  out[1] = a[1] - b[1];\n  out[2] = a[2] - b[2];\n  out[3] = a[3] - b[3];\n  return out;\n}\n\n/**\n * Multiplies two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function multiply(out, a, b) {\n  out[0] = a[0] * b[0];\n  out[1] = a[1] * b[1];\n  out[2] = a[2] * b[2];\n  out[3] = a[3] * b[3];\n  return out;\n}\n\n/**\n * Divides two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function divide(out, a, b) {\n  out[0] = a[0] / b[0];\n  out[1] = a[1] / b[1];\n  out[2] = a[2] / b[2];\n  out[3] = a[3] / b[3];\n  return out;\n}\n\n/**\n * Math.ceil the components of a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to ceil\n * @returns {vec4} out\n */\nexport function ceil(out, a) {\n  out[0] = Math.ceil(a[0]);\n  out[1] = Math.ceil(a[1]);\n  out[2] = Math.ceil(a[2]);\n  out[3] = Math.ceil(a[3]);\n  return out;\n}\n\n/**\n * Math.floor the components of a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to floor\n * @returns {vec4} out\n */\nexport function floor(out, a) {\n  out[0] = Math.floor(a[0]);\n  out[1] = Math.floor(a[1]);\n  out[2] = Math.floor(a[2]);\n  out[3] = Math.floor(a[3]);\n  return out;\n}\n\n/**\n * Returns the minimum of two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function min(out, a, b) {\n  out[0] = Math.min(a[0], b[0]);\n  out[1] = Math.min(a[1], b[1]);\n  out[2] = Math.min(a[2], b[2]);\n  out[3] = Math.min(a[3], b[3]);\n  return out;\n}\n\n/**\n * Returns the maximum of two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {vec4} out\n */\nexport function max(out, a, b) {\n  out[0] = Math.max(a[0], b[0]);\n  out[1] = Math.max(a[1], b[1]);\n  out[2] = Math.max(a[2], b[2]);\n  out[3] = Math.max(a[3], b[3]);\n  return out;\n}\n\n/**\n * Math.round the components of a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to round\n * @returns {vec4} out\n */\nexport function round(out, a) {\n  out[0] = Math.round(a[0]);\n  out[1] = Math.round(a[1]);\n  out[2] = Math.round(a[2]);\n  out[3] = Math.round(a[3]);\n  return out;\n}\n\n/**\n * Scales a vec4 by a scalar number\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the vector to scale\n * @param {Number} b amount to scale the vector by\n * @returns {vec4} out\n */\nexport function scale(out, a, b) {\n  out[0] = a[0] * b;\n  out[1] = a[1] * b;\n  out[2] = a[2] * b;\n  out[3] = a[3] * b;\n  return out;\n}\n\n/**\n * Adds two vec4's after scaling the second operand by a scalar value\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @param {Number} scale the amount to scale b by before adding\n * @returns {vec4} out\n */\nexport function scaleAndAdd(out, a, b, scale) {\n  out[0] = a[0] + (b[0] * scale);\n  out[1] = a[1] + (b[1] * scale);\n  out[2] = a[2] + (b[2] * scale);\n  out[3] = a[3] + (b[3] * scale);\n  return out;\n}\n\n/**\n * Calculates the euclidian distance between two vec4's\n *\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {Number} distance between a and b\n */\nexport function distance(a, b) {\n  let x = b[0] - a[0];\n  let y = b[1] - a[1];\n  let z = b[2] - a[2];\n  let w = b[3] - a[3];\n  return Math.sqrt(x*x + y*y + z*z + w*w);\n}\n\n/**\n * Calculates the squared euclidian distance between two vec4's\n *\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {Number} squared distance between a and b\n */\nexport function squaredDistance(a, b) {\n  let x = b[0] - a[0];\n  let y = b[1] - a[1];\n  let z = b[2] - a[2];\n  let w = b[3] - a[3];\n  return x*x + y*y + z*z + w*w;\n}\n\n/**\n * Calculates the length of a vec4\n *\n * @param {vec4} a vector to calculate length of\n * @returns {Number} length of a\n */\nexport function length(a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  let w = a[3];\n  return Math.sqrt(x*x + y*y + z*z + w*w);\n}\n\n/**\n * Calculates the squared length of a vec4\n *\n * @param {vec4} a vector to calculate squared length of\n * @returns {Number} squared length of a\n */\nexport function squaredLength(a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  let w = a[3];\n  return x*x + y*y + z*z + w*w;\n}\n\n/**\n * Negates the components of a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to negate\n * @returns {vec4} out\n */\nexport function negate(out, a) {\n  out[0] = -a[0];\n  out[1] = -a[1];\n  out[2] = -a[2];\n  out[3] = -a[3];\n  return out;\n}\n\n/**\n * Returns the inverse of the components of a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to invert\n * @returns {vec4} out\n */\nexport function inverse(out, a) {\n  out[0] = 1.0 / a[0];\n  out[1] = 1.0 / a[1];\n  out[2] = 1.0 / a[2];\n  out[3] = 1.0 / a[3];\n  return out;\n}\n\n/**\n * Normalize a vec4\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a vector to normalize\n * @returns {vec4} out\n */\nexport function normalize(out, a) {\n  let x = a[0];\n  let y = a[1];\n  let z = a[2];\n  let w = a[3];\n  let len = x*x + y*y + z*z + w*w;\n  if (len > 0) {\n    len = 1 / Math.sqrt(len);\n    out[0] = x * len;\n    out[1] = y * len;\n    out[2] = z * len;\n    out[3] = w * len;\n  }\n  return out;\n}\n\n/**\n * Calculates the dot product of two vec4's\n *\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @returns {Number} dot product of a and b\n */\nexport function dot(a, b) {\n  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];\n}\n\n/**\n * Performs a linear interpolation between two vec4's\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the first operand\n * @param {vec4} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec4} out\n */\nexport function lerp(out, a, b, t) {\n  let ax = a[0];\n  let ay = a[1];\n  let az = a[2];\n  let aw = a[3];\n  out[0] = ax + t * (b[0] - ax);\n  out[1] = ay + t * (b[1] - ay);\n  out[2] = az + t * (b[2] - az);\n  out[3] = aw + t * (b[3] - aw);\n  return out;\n}\n\n/**\n * Generates a random vector with the given scale\n *\n * @param {vec4} out the receiving vector\n * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned\n * @returns {vec4} out\n */\nexport function random(out, scale) {\n  scale = scale || 1.0;\n\n  // Marsaglia, George. Choosing a Point from the Surface of a\n  // Sphere. Ann. Math. Statist. 43 (1972), no. 2, 645--646.\n  // http://projecteuclid.org/euclid.aoms/1177692644;\n  var v1, v2, v3, v4;\n  var s1, s2;\n  do {\n    v1 = glMatrix.RANDOM() * 2 - 1;\n    v2 = glMatrix.RANDOM() * 2 - 1;\n    s1 = v1 * v1 + v2 * v2;\n  } while (s1 >= 1);\n  do {\n    v3 = glMatrix.RANDOM() * 2 - 1;\n    v4 = glMatrix.RANDOM() * 2 - 1;\n    s2 = v3 * v3 + v4 * v4;\n  } while (s2 >= 1);\n\n  var d = Math.sqrt((1 - s1) / s2);\n  out[0] = scale * v1;\n  out[1] = scale * v2;\n  out[2] = scale * v3 * d;\n  out[3] = scale * v4 * d;\n  return out;\n}\n\n/**\n * Transforms the vec4 with a mat4.\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the vector to transform\n * @param {mat4} m matrix to transform with\n * @returns {vec4} out\n */\nexport function transformMat4(out, a, m) {\n  let x = a[0], y = a[1], z = a[2], w = a[3];\n  out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;\n  out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;\n  out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;\n  out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;\n  return out;\n}\n\n/**\n * Transforms the vec4 with a quat\n *\n * @param {vec4} out the receiving vector\n * @param {vec4} a the vector to transform\n * @param {quat} q quaternion to transform with\n * @returns {vec4} out\n */\nexport function transformQuat(out, a, q) {\n  let x = a[0], y = a[1], z = a[2];\n  let qx = q[0], qy = q[1], qz = q[2], qw = q[3];\n\n  // calculate quat * vec\n  let ix = qw * x + qy * z - qz * y;\n  let iy = qw * y + qz * x - qx * z;\n  let iz = qw * z + qx * y - qy * x;\n  let iw = -qx * x - qy * y - qz * z;\n\n  // calculate result * inverse quat\n  out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;\n  out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;\n  out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;\n  out[3] = a[3];\n  return out;\n}\n\n/**\n * Returns a string representation of a vector\n *\n * @param {vec4} a vector to represent as a string\n * @returns {String} string representation of the vector\n */\nexport function str(a) {\n  return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';\n}\n\n/**\n * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)\n *\n * @param {vec4} a The first vector.\n * @param {vec4} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function exactEquals(a, b) {\n  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];\n}\n\n/**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n *\n * @param {vec4} a The first vector.\n * @param {vec4} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\nexport function equals(a, b) {\n  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));\n}\n\n/**\n * Alias for {@link vec4.subtract}\n * @function\n */\nexport const sub = subtract;\n\n/**\n * Alias for {@link vec4.multiply}\n * @function\n */\nexport const mul = multiply;\n\n/**\n * Alias for {@link vec4.divide}\n * @function\n */\nexport const div = divide;\n\n/**\n * Alias for {@link vec4.distance}\n * @function\n */\nexport const dist = distance;\n\n/**\n * Alias for {@link vec4.squaredDistance}\n * @function\n */\nexport const sqrDist = squaredDistance;\n\n/**\n * Alias for {@link vec4.length}\n * @function\n */\nexport const len = length;\n\n/**\n * Alias for {@link vec4.squaredLength}\n * @function\n */\nexport const sqrLen = squaredLength;\n\n/**\n * Perform some operation over an array of vec4s.\n *\n * @param {Array} a the array of vectors to iterate over\n * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed\n * @param {Number} offset Number of elements to skip at the beginning of the array\n * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array\n * @param {Function} fn Function to call for each vector in the array\n * @param {Object} [arg] additional argument to pass to fn\n * @returns {Array} a\n * @function\n */\nexport const forEach = (function() {\n  let vec = create();\n\n  return function(a, stride, offset, count, fn, arg) {\n    let i, l;\n    if(!stride) {\n      stride = 4;\n    }\n\n    if(!offset) {\n      offset = 0;\n    }\n\n    if(count) {\n      l = Math.min((count * stride) + offset, a.length);\n    } else {\n      l = a.length;\n    }\n\n    for(i = offset; i < l; i += stride) {\n      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3];\n      fn(vec, vec, arg);\n      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3];\n    }\n\n    return a;\n  };\n})();\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nconst GL = WebGLRenderingContext; // For enums\n\nexport const CAP = {\n  // Enable caps\n  CULL_FACE: 0x001,\n  BLEND: 0x002,\n  DEPTH_TEST: 0x004,\n  STENCIL_TEST: 0x008,\n  COLOR_MASK: 0x010,\n  DEPTH_MASK: 0x020,\n  STENCIL_MASK: 0x040,\n};\n\nexport const MAT_STATE = {\n  CAPS_RANGE: 0x000000FF,\n  BLEND_SRC_SHIFT: 8,\n  BLEND_SRC_RANGE: 0x00000F00,\n  BLEND_DST_SHIFT: 12,\n  BLEND_DST_RANGE: 0x0000F000,\n  BLEND_FUNC_RANGE: 0x0000FF00,\n  DEPTH_FUNC_SHIFT: 16,\n  DEPTH_FUNC_RANGE: 0x000F0000,\n};\n\nexport const RENDER_ORDER = {\n  // Render opaque objects first.\n  OPAQUE: 0,\n\n  // Render the sky after all opaque object to save fill rate.\n  SKY: 1,\n\n  // Render transparent objects next so that the opaqe objects show through.\n  TRANSPARENT: 2,\n\n  // Finally render purely additive effects like pointer rays so that they\n  // can render without depth mask.\n  ADDITIVE: 3,\n\n  // Render order will be picked based on the material properties.\n  DEFAULT: 4,\n};\n\nexport function stateToBlendFunc(state, mask, shift) {\n  let value = (state & mask) >> shift;\n  switch (value) {\n    case 0:\n    case 1:\n      return value;\n    default:\n      return (value - 2) + GL.SRC_COLOR;\n  }\n}\n\nexport class MaterialState {\n  constructor() {\n    this._state = CAP.CULL_FACE |\n                  CAP.DEPTH_TEST |\n                  CAP.COLOR_MASK |\n                  CAP.DEPTH_MASK;\n\n    // Use a fairly commonly desired blend func as the default.\n    this.blendFuncSrc = GL.SRC_ALPHA;\n    this.blendFuncDst = GL.ONE_MINUS_SRC_ALPHA;\n\n    this.depthFunc = GL.LESS;\n  }\n\n  get cullFace() {\n    return !!(this._state & CAP.CULL_FACE);\n  }\n  set cullFace(value) {\n    if (value) {\n      this._state |= CAP.CULL_FACE;\n    } else {\n      this._state &= ~CAP.CULL_FACE;\n    }\n  }\n\n  get blend() {\n    return !!(this._state & CAP.BLEND);\n  }\n  set blend(value) {\n    if (value) {\n      this._state |= CAP.BLEND;\n    } else {\n      this._state &= ~CAP.BLEND;\n    }\n  }\n\n  get depthTest() {\n    return !!(this._state & CAP.DEPTH_TEST);\n  }\n  set depthTest(value) {\n    if (value) {\n      this._state |= CAP.DEPTH_TEST;\n    } else {\n      this._state &= ~CAP.DEPTH_TEST;\n    }\n  }\n\n  get stencilTest() {\n    return !!(this._state & CAP.STENCIL_TEST);\n  }\n  set stencilTest(value) {\n    if (value) {\n      this._state |= CAP.STENCIL_TEST;\n    } else {\n      this._state &= ~CAP.STENCIL_TEST;\n    }\n  }\n\n  get colorMask() {\n    return !!(this._state & CAP.COLOR_MASK);\n  }\n  set colorMask(value) {\n    if (value) {\n      this._state |= CAP.COLOR_MASK;\n    } else {\n      this._state &= ~CAP.COLOR_MASK;\n    }\n  }\n\n  get depthMask() {\n    return !!(this._state & CAP.DEPTH_MASK);\n  }\n  set depthMask(value) {\n    if (value) {\n      this._state |= CAP.DEPTH_MASK;\n    } else {\n      this._state &= ~CAP.DEPTH_MASK;\n    }\n  }\n\n  get depthFunc() {\n    return ((this._state & MAT_STATE.DEPTH_FUNC_RANGE) >> MAT_STATE.DEPTH_FUNC_SHIFT) + GL.NEVER;\n  }\n  set depthFunc(value) {\n    value = value - GL.NEVER;\n    this._state &= ~MAT_STATE.DEPTH_FUNC_RANGE;\n    this._state |= (value << MAT_STATE.DEPTH_FUNC_SHIFT);\n  }\n\n  get stencilMask() {\n    return !!(this._state & CAP.STENCIL_MASK);\n  }\n  set stencilMask(value) {\n    if (value) {\n      this._state |= CAP.STENCIL_MASK;\n    } else {\n      this._state &= ~CAP.STENCIL_MASK;\n    }\n  }\n\n  get blendFuncSrc() {\n    return stateToBlendFunc(this._state, MAT_STATE.BLEND_SRC_RANGE, MAT_STATE.BLEND_SRC_SHIFT);\n  }\n  set blendFuncSrc(value) {\n    switch (value) {\n      case 0:\n      case 1:\n        break;\n      default:\n        value = (value - GL.SRC_COLOR) + 2;\n    }\n    this._state &= ~MAT_STATE.BLEND_SRC_RANGE;\n    this._state |= (value << MAT_STATE.BLEND_SRC_SHIFT);\n  }\n\n  get blendFuncDst() {\n    return stateToBlendFunc(this._state, MAT_STATE.BLEND_DST_RANGE, MAT_STATE.BLEND_DST_SHIFT);\n  }\n  set blendFuncDst(value) {\n    switch (value) {\n      case 0:\n      case 1:\n        break;\n      default:\n        value = (value - GL.SRC_COLOR) + 2;\n    }\n    this._state &= ~MAT_STATE.BLEND_DST_RANGE;\n    this._state |= (value << MAT_STATE.BLEND_DST_SHIFT);\n  }\n}\n\nclass MaterialSampler {\n  constructor(uniformName) {\n    this._uniformName = uniformName;\n    this._texture = null;\n  }\n\n  get texture() {\n    return this._texture;\n  }\n\n  set texture(value) {\n    this._texture = value;\n  }\n}\n\nclass MaterialUniform {\n  constructor(uniformName, defaultValue, length) {\n    this._uniformName = uniformName;\n    this._value = defaultValue;\n    this._length = length;\n    if (!this._length) {\n      if (defaultValue instanceof Array) {\n        this._length = defaultValue.length;\n      } else {\n        this._length = 1;\n      }\n    }\n  }\n\n  get value() {\n    return this._value;\n  }\n\n  set value(value) {\n    this._value = value;\n  }\n}\n\nexport class Material {\n  constructor() {\n    this.state = new MaterialState;\n    this.renderOrder = RENDER_ORDER.DEFAULT;\n    this._samplers = [];\n    this._uniforms = [];\n  }\n\n  defineSampler(uniformName) {\n    let sampler = new MaterialSampler(uniformName);\n    this._samplers.push(sampler);\n    return sampler;\n  }\n\n  defineUniform(uniformName, defaultValue=null, length=0) {\n    let uniform = new MaterialUniform(uniformName, defaultValue, length);\n    this._uniforms.push(uniform);\n    return uniform;\n  }\n\n  get materialName() {\n    return null;\n  }\n\n  get vertexSource() {\n    return null;\n  }\n\n  get fragmentSource() {\n    return null;\n  }\n\n  getProgramDefines(renderPrimitive) {\n    return {};\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Ray} from '../math/ray.js';\nimport {mat4, vec3, quat} from '../math/gl-matrix.js';\n\nconst DEFAULT_TRANSLATION = new Float32Array([0, 0, 0]);\nconst DEFAULT_ROTATION = new Float32Array([0, 0, 0, 1]);\nconst DEFAULT_SCALE = new Float32Array([1, 1, 1]);\n\nlet tmpRayMatrix = mat4.create();\n\nexport class Node {\n  constructor() {\n    this.name = null; // Only for debugging\n    this.children = [];\n    this.parent = null;\n    this.visible = true;\n    this.selectable = false;\n\n    this._matrix = null;\n\n    this._dirtyTRS = false;\n    this._translation = null;\n    this._rotation = null;\n    this._scale = null;\n\n    this._dirtyWorldMatrix = false;\n    this._worldMatrix = null;\n\n    this._activeFrameId = -1;\n    this._hoverFrameId = -1;\n    this._renderPrimitives = null;\n    this._renderer = null;\n\n    this._selectHandler = null;\n  }\n\n  _setRenderer(renderer) {\n    if (this._renderer == renderer) {\n      return;\n    }\n\n    if (this._renderer) {\n      // Changing the renderer removes any previously attached renderPrimitives\n      // from a different renderer.\n      this.clearRenderPrimitives();\n    }\n\n    this._renderer = renderer;\n    if (renderer) {\n      this.onRendererChanged(renderer);\n\n      for (let child of this.children) {\n        child._setRenderer(renderer);\n      }\n    }\n  }\n\n  onRendererChanged(renderer) {\n    // Override in other node types to respond to changes in the renderer.\n  }\n\n  // Create a clone of this node and all of it's children. Does not duplicate\n  // RenderPrimitives, the cloned nodes will be treated as new instances of the\n  // geometry.\n  clone() {\n    let cloneNode = new Node();\n    cloneNode.name = this.name;\n    cloneNode.visible = this.visible;\n    cloneNode._renderer = this._renderer;\n\n    cloneNode._dirtyTRS = this._dirtyTRS;\n\n    if (this._translation) {\n      cloneNode._translation = vec3.create();\n      vec3.copy(cloneNode._translation, this._translation);\n    }\n\n    if (this._rotation) {\n      cloneNode._rotation = quat.create();\n      quat.copy(cloneNode._rotation, this._rotation);\n    }\n\n    if (this._scale) {\n      cloneNode._scale = vec3.create();\n      vec3.copy(cloneNode._scale, this._scale);\n    }\n\n    // Only copy the matrices if they're not already dirty.\n    if (!cloneNode._dirtyTRS && this._matrix) {\n      cloneNode._matrix = mat4.create();\n      mat4.copy(cloneNode._matrix, this._matrix);\n    }\n\n    cloneNode._dirtyWorldMatrix = this._dirtyWorldMatrix;\n    if (!cloneNode._dirtyWorldMatrix && this._worldMatrix) {\n      cloneNode._worldMatrix = mat4.create();\n      mat4.copy(cloneNode._worldMatrix, this._worldMatrix);\n    }\n\n    this.waitForComplete().then(() => {\n      if (this._renderPrimitives) {\n        for (let primitive of this._renderPrimitives) {\n          cloneNode.addRenderPrimitive(primitive);\n        }\n      }\n\n      for (let child of this.children) {\n        cloneNode.addNode(child.clone());\n      }\n    });\n\n    return cloneNode;\n  }\n\n  markActive(frameId) {\n    if (this.visible && this._renderPrimitives) {\n      this._activeFrameId = frameId;\n      for (let primitive of this._renderPrimitives) {\n        primitive.markActive(frameId);\n      }\n    }\n\n    for (let child of this.children) {\n      if (child.visible) {\n        child.markActive(frameId);\n      }\n    }\n  }\n\n  addNode(value) {\n    if (!value || value.parent == this) {\n      return;\n    }\n\n    if (value.parent) {\n      value.parent.removeNode(value);\n    }\n    value.parent = this;\n\n    this.children.push(value);\n\n    if (this._renderer) {\n      value._setRenderer(this._renderer);\n    }\n  }\n\n  removeNode(value) {\n    let i = this.children.indexOf(value);\n    if (i > -1) {\n      this.children.splice(i, 1);\n      value.parent = null;\n    }\n  }\n\n  clearNodes() {\n    for (let child of this.children) {\n      child.parent = null;\n    }\n    this.children = [];\n  }\n\n  setMatrixDirty() {\n    if (!this._dirtyWorldMatrix) {\n      this._dirtyWorldMatrix = true;\n      for (let child of this.children) {\n        child.setMatrixDirty();\n      }\n    }\n  }\n\n  _updateLocalMatrix() {\n    if (!this._matrix) {\n      this._matrix = mat4.create();\n    }\n\n    if (this._dirtyTRS) {\n      this._dirtyTRS = false;\n      mat4.fromRotationTranslationScale(\n        this._matrix,\n        this._rotation || DEFAULT_ROTATION,\n        this._translation || DEFAULT_TRANSLATION,\n        this._scale || DEFAULT_SCALE);\n    }\n\n    return this._matrix;\n  }\n\n  set matrix(value) {\n    if (value) {\n      if (!this._matrix) {\n        this._matrix = mat4.create();\n      }\n      mat4.copy(this._matrix, value);\n    } else {\n      this._matrix = null;\n    }\n    this.setMatrixDirty();\n    this._dirtyTRS = false;\n    this._translation = null;\n    this._rotation = null;\n    this._scale = null;\n  }\n\n  get matrix() {\n    this.setMatrixDirty();\n\n    return this._updateLocalMatrix();\n  }\n\n  get worldMatrix() {\n    if (!this._worldMatrix) {\n      this._dirtyWorldMatrix = true;\n      this._worldMatrix = mat4.create();\n    }\n\n    if (this._dirtyWorldMatrix || this._dirtyTRS) {\n      if (this.parent) {\n        // TODO: Some optimizations that could be done here if the node matrix\n        // is an identity matrix.\n        mat4.mul(this._worldMatrix, this.parent.worldMatrix, this._updateLocalMatrix());\n      } else {\n        mat4.copy(this._worldMatrix, this._updateLocalMatrix());\n      }\n      this._dirtyWorldMatrix = false;\n    }\n\n    return this._worldMatrix;\n  }\n\n  // TODO: Decompose matrix when fetching these?\n  set translation(value) {\n    if (value != null) {\n      this._dirtyTRS = true;\n      this.setMatrixDirty();\n    }\n    this._translation = value;\n  }\n\n  get translation() {\n    this._dirtyTRS = true;\n    this.setMatrixDirty();\n    if (!this._translation) {\n      this._translation = vec3.clone(DEFAULT_TRANSLATION);\n    }\n    return this._translation;\n  }\n\n  set rotation(value) {\n    if (value != null) {\n      this._dirtyTRS = true;\n      this.setMatrixDirty();\n    }\n    this._rotation = value;\n  }\n\n  get rotation() {\n    this._dirtyTRS = true;\n    this.setMatrixDirty();\n    if (!this._rotation) {\n      this._rotation = quat.clone(DEFAULT_ROTATION);\n    }\n    return this._rotation;\n  }\n\n  set scale(value) {\n    if (value != null) {\n      this._dirtyTRS = true;\n      this.setMatrixDirty();\n    }\n    this._scale = value;\n  }\n\n  get scale() {\n    this._dirtyTRS = true;\n    this.setMatrixDirty();\n    if (!this._scale) {\n      this._scale = vec3.clone(DEFAULT_SCALE);\n    }\n    return this._scale;\n  }\n\n  waitForComplete() {\n    let childPromises = [];\n    for (let child of this.children) {\n      childPromises.push(child.waitForComplete());\n    }\n    if (this._renderPrimitives) {\n      for (let primitive of this._renderPrimitives) {\n        childPromises.push(primitive.waitForComplete());\n      }\n    }\n    return Promise.all(childPromises).then(() => this);\n  }\n\n  get renderPrimitives() {\n    return this._renderPrimitives;\n  }\n\n  addRenderPrimitive(primitive) {\n    if (!this._renderPrimitives) {\n      this._renderPrimitives = [primitive];\n    } else {\n      this._renderPrimitives.push(primitive);\n    }\n    primitive._instances.push(this);\n  }\n\n  removeRenderPrimitive(primitive) {\n    if (!this._renderPrimitives) {\n      return;\n    }\n\n    let index = this._renderPrimitives._instances.indexOf(primitive);\n    if (index > -1) {\n      this._renderPrimitives._instances.splice(index, 1);\n\n      index = primitive._instances.indexOf(this);\n      if (index > -1) {\n        primitive._instances.splice(index, 1);\n      }\n\n      if (!this._renderPrimitives.length) {\n        this._renderPrimitives = null;\n      }\n    }\n  }\n\n  clearRenderPrimitives() {\n    if (this._renderPrimitives) {\n      for (let primitive of this._renderPrimitives) {\n        let index = primitive._instances.indexOf(this);\n        if (index > -1) {\n          primitive._instances.splice(index, 1);\n        }\n      }\n      this._renderPrimitives = null;\n    }\n  }\n\n  _hitTestSelectableNode(ray) {\n    if (this._renderPrimitives) {\n      let localRay = null;\n      for (let primitive of this._renderPrimitives) {\n        if (primitive._min) {\n          if (!localRay) {\n            mat4.invert(tmpRayMatrix, this.worldMatrix);\n            mat4.multiply(tmpRayMatrix, tmpRayMatrix, ray.transformMatrix);\n            localRay = new Ray(tmpRayMatrix);\n          }\n          let intersection = localRay.intersectsAABB(primitive._min, primitive._max);\n          if (intersection) {\n            vec3.transformMat4(intersection, intersection, this.worldMatrix);\n            return intersection;\n          }\n        }\n      }\n    }\n    for (let child of this.children) {\n      let intersection = child._hitTestSelectableNode(ray);\n      if (intersection) {\n        return intersection;\n      }\n    }\n    return null;\n  }\n\n  hitTest(ray) {\n    if (this.selectable && this.visible) {\n      let intersection = this._hitTestSelectableNode(ray);\n\n      if (intersection) {\n        let origin = vec3.fromValues(ray.origin.x, ray.origin.y, ray.origin.z);\n        return {\n          node: this,\n          intersection: intersection,\n          distance: vec3.distance(origin, intersection),\n        };\n      }\n      return null;\n    }\n\n    let result = null;\n    for (let child of this.children) {\n      let childResult = child.hitTest(ray);\n      if (childResult) {\n        if (!result || result.distance > childResult.distance) {\n          result = childResult;\n        }\n      }\n    }\n    return result;\n  }\n\n  onSelect(value) {\n    this._selectHandler = value;\n  }\n\n  get selectHandler() {\n    return this._selectHandler;\n  }\n\n  // Called when a selectable node is selected.\n  handleSelect() {\n    if (this._selectHandler) {\n      this._selectHandler();\n    }\n  }\n\n  // Called when a selectable element is pointed at.\n  onHoverStart() {\n\n  }\n\n  // Called when a selectable element is no longer pointed at.\n  onHoverEnd() {\n\n  }\n\n  _update(timestamp, frameDelta) {\n    this.onUpdate(timestamp, frameDelta);\n\n    for (let child of this.children) {\n      child._update(timestamp, frameDelta);\n    }\n  }\n\n  // Called every frame so that the nodes can animate themselves\n  onUpdate(timestamp, frameDelta) {\n\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {vec3} from '../math/gl-matrix.js';\n\nexport class PrimitiveAttribute {\n  constructor(name, buffer, componentCount, componentType, stride, byteOffset) {\n    this.name = name;\n    this.buffer = buffer;\n    this.componentCount = componentCount || 3;\n    this.componentType = componentType || 5126; // gl.FLOAT;\n    this.stride = stride || 0;\n    this.byteOffset = byteOffset || 0;\n    this.normalized = false;\n  }\n}\n\nexport class Primitive {\n  constructor(attributes, elementCount, mode) {\n    this.attributes = attributes || [];\n    this.elementCount = elementCount || 0;\n    this.mode = mode || 4; // gl.TRIANGLES;\n    this.indexBuffer = null;\n    this.indexByteOffset = 0;\n    this.indexType = 0;\n    this._min = null;\n    this._max = null;\n  }\n\n  setIndexBuffer(indexBuffer, byteOffset, indexType) {\n    this.indexBuffer = indexBuffer;\n    this.indexByteOffset = byteOffset || 0;\n    this.indexType = indexType || 5123; // gl.UNSIGNED_SHORT;\n  }\n\n  setBounds(min, max) {\n    this._min = vec3.clone(min);\n    this._max = vec3.clone(max);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nexport class Program {\n  constructor(gl, vertSrc, fragSrc, attribMap, defines) {\n    this._gl = gl;\n    this.program = gl.createProgram();\n    this.attrib = null;\n    this.uniform = null;\n    this.defines = {};\n\n    this._firstUse = true;\n    this._nextUseCallbacks = [];\n\n    let definesString = '';\n    if (defines) {\n      for (let define in defines) {\n        this.defines[define] = defines[define];\n        definesString += `#define ${define} ${defines[define]}\\n`;\n      }\n    }\n\n    this._vertShader = gl.createShader(gl.VERTEX_SHADER);\n    gl.attachShader(this.program, this._vertShader);\n    gl.shaderSource(this._vertShader, definesString + vertSrc);\n    gl.compileShader(this._vertShader);\n\n    this._fragShader = gl.createShader(gl.FRAGMENT_SHADER);\n    gl.attachShader(this.program, this._fragShader);\n    gl.shaderSource(this._fragShader, definesString + fragSrc);\n    gl.compileShader(this._fragShader);\n\n    if (attribMap) {\n      this.attrib = {};\n      for (let attribName in attribMap) {\n        gl.bindAttribLocation(this.program, attribMap[attribName], attribName);\n        this.attrib[attribName] = attribMap[attribName];\n      }\n    }\n\n    gl.linkProgram(this.program);\n  }\n\n  onNextUse(callback) {\n    this._nextUseCallbacks.push(callback);\n  }\n\n  use() {\n    let gl = this._gl;\n\n    // If this is the first time the program has been used do all the error checking and\n    // attrib/uniform querying needed.\n    if (this._firstUse) {\n      this._firstUse = false;\n      if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {\n        if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) {\n          console.error('Vertex shader compile error: ' + gl.getShaderInfoLog(this._vertShader));\n        } else if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) {\n          console.error('Fragment shader compile error: ' + gl.getShaderInfoLog(this._fragShader));\n        } else {\n          console.error('Program link error: ' + gl.getProgramInfoLog(this.program));\n        }\n        gl.deleteProgram(this.program);\n        this.program = null;\n      } else {\n        if (!this.attrib) {\n          this.attrib = {};\n          let attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES);\n          for (let i = 0; i < attribCount; i++) {\n            let attribInfo = gl.getActiveAttrib(this.program, i);\n            this.attrib[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name);\n          }\n        }\n\n        this.uniform = {};\n        let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);\n        let uniformName = '';\n        for (let i = 0; i < uniformCount; i++) {\n          let uniformInfo = gl.getActiveUniform(this.program, i);\n          uniformName = uniformInfo.name.replace('[0]', '');\n          this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName);\n        }\n      }\n      gl.deleteShader(this._vertShader);\n      gl.deleteShader(this._fragShader);\n    }\n\n    gl.useProgram(this.program);\n\n    if (this._nextUseCallbacks.length) {\n      for (let callback of this._nextUseCallbacks) {\n        callback(this);\n      }\n      this._nextUseCallbacks = [];\n    }\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {CAP, MAT_STATE, RENDER_ORDER, stateToBlendFunc} from './material.js';\nimport {Node} from './node.js';\nimport {Program} from './program.js';\nimport {DataTexture, VideoTexture} from './texture.js';\nimport {mat4, vec3} from '../math/gl-matrix.js';\n\nexport const ATTRIB = {\n  POSITION: 1,\n  NORMAL: 2,\n  TANGENT: 3,\n  TEXCOORD_0: 4,\n  TEXCOORD_1: 5,\n  COLOR_0: 6,\n};\n\nexport const ATTRIB_MASK = {\n  POSITION: 0x0001,\n  NORMAL: 0x0002,\n  TANGENT: 0x0004,\n  TEXCOORD_0: 0x0008,\n  TEXCOORD_1: 0x0010,\n  COLOR_0: 0x0020,\n};\n\nconst GL = WebGLRenderingContext; // For enums\n\nconst DEF_LIGHT_DIR = new Float32Array([-0.1, -1.0, -0.2]);\nconst DEF_LIGHT_COLOR = new Float32Array([3.0, 3.0, 3.0]);\n\nconst PRECISION_REGEX = new RegExp('precision (lowp|mediump|highp) float;');\n\nconst VERTEX_SHADER_SINGLE_ENTRY = `\nuniform mat4 PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX;\n\nvoid main() {\n  gl_Position = vertex_main(PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX);\n}\n`;\n\nconst VERTEX_SHADER_MULTI_ENTRY = `\n#ERROR Multiview rendering is not implemented\nvoid main() {\n  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n}\n`;\n\nconst FRAGMENT_SHADER_ENTRY = `\nvoid main() {\n  gl_FragColor = fragment_main();\n}\n`;\n\nfunction isPowerOfTwo(n) {\n  return (n & (n - 1)) === 0;\n}\n\n// Creates a WebGL context and initializes it with some common default state.\nexport function createWebGLContext(glAttribs) {\n  glAttribs = glAttribs || {alpha: false};\n\n  let webglCanvas = document.createElement('canvas');\n  let contextTypes = glAttribs.webgl2 ? ['webgl2'] : ['webgl', 'experimental-webgl'];\n  let context = null;\n\n  for (let contextType of contextTypes) {\n    context = webglCanvas.getContext(contextType, glAttribs);\n    if (context) {\n      break;\n    }\n  }\n\n  if (!context) {\n    let webglType = (glAttribs.webgl2 ? 'WebGL 2' : 'WebGL');\n    console.error('This browser does not support ' + webglType + '.');\n    return null;\n  }\n\n  return context;\n}\n\nexport class RenderView {\n  constructor(projectionMatrix, viewMatrix, viewport = null, eye = 'left') {\n    this.projectionMatrix = projectionMatrix;\n    this.viewMatrix = viewMatrix;\n    this.viewport = viewport;\n    // If an eye isn't given the left eye is assumed.\n    this._eye = eye;\n    this._eyeIndex = (eye == 'left' ? 0 : 1);\n  }\n\n  get eye() {\n    return this._eye;\n  }\n\n  set eye(value) {\n    this._eye = value;\n    this._eyeIndex = (value == 'left' ? 0 : 1);\n  }\n\n  get eyeIndex() {\n    return this._eyeIndex;\n  }\n}\n\nclass RenderBuffer {\n  constructor(target, usage, buffer, length = 0) {\n    this._target = target;\n    this._usage = usage;\n    this._length = length;\n    if (buffer instanceof Promise) {\n      this._buffer = null;\n      this._promise = buffer.then((buffer) => {\n        this._buffer = buffer;\n        return this;\n      });\n    } else {\n      this._buffer = buffer;\n      this._promise = Promise.resolve(this);\n    }\n  }\n\n  waitForComplete() {\n    return this._promise;\n  }\n}\n\nclass RenderPrimitiveAttribute {\n  constructor(primitiveAttribute) {\n    this._attrib_index = ATTRIB[primitiveAttribute.name];\n    this._componentCount = primitiveAttribute.componentCount;\n    this._componentType = primitiveAttribute.componentType;\n    this._stride = primitiveAttribute.stride;\n    this._byteOffset = primitiveAttribute.byteOffset;\n    this._normalized = primitiveAttribute.normalized;\n  }\n}\n\nclass RenderPrimitiveAttributeBuffer {\n  constructor(buffer) {\n    this._buffer = buffer;\n    this._attributes = [];\n  }\n}\n\nclass RenderPrimitive {\n  constructor(primitive) {\n    this._activeFrameId = 0;\n    this._instances = [];\n    this._material = null;\n\n    this.setPrimitive(primitive);\n  }\n\n  setPrimitive(primitive) {\n    this._mode = primitive.mode;\n    this._elementCount = primitive.elementCount;\n    this._promise = null;\n    this._vao = null;\n    this._complete = false;\n    this._attributeBuffers = [];\n    this._attributeMask = 0;\n\n    for (let attribute of primitive.attributes) {\n      this._attributeMask |= ATTRIB_MASK[attribute.name];\n      let renderAttribute = new RenderPrimitiveAttribute(attribute);\n      let foundBuffer = false;\n      for (let attributeBuffer of this._attributeBuffers) {\n        if (attributeBuffer._buffer == attribute.buffer) {\n          attributeBuffer._attributes.push(renderAttribute);\n          foundBuffer = true;\n          break;\n        }\n      }\n      if (!foundBuffer) {\n        let attributeBuffer = new RenderPrimitiveAttributeBuffer(attribute.buffer);\n        attributeBuffer._attributes.push(renderAttribute);\n        this._attributeBuffers.push(attributeBuffer);\n      }\n    }\n\n    this._indexBuffer = null;\n    this._indexByteOffset = 0;\n    this._indexType = 0;\n\n    if (primitive.indexBuffer) {\n      this._indexByteOffset = primitive.indexByteOffset;\n      this._indexType = primitive.indexType;\n      this._indexBuffer = primitive.indexBuffer;\n    }\n\n    if (primitive._min) {\n      this._min = vec3.clone(primitive._min);\n      this._max = vec3.clone(primitive._max);\n    } else {\n      this._min = null;\n      this._max = null;\n    }\n\n    if (this._material != null) {\n      this.waitForComplete(); // To flip the _complete flag.\n    }\n  }\n\n  setRenderMaterial(material) {\n    this._material = material;\n    this._promise = null;\n    this._complete = false;\n\n    if (this._material != null) {\n      this.waitForComplete(); // To flip the _complete flag.\n    }\n  }\n\n  markActive(frameId) {\n    if (this._complete && this._activeFrameId != frameId) {\n      if (this._material) {\n        if (!this._material.markActive(frameId)) {\n          return;\n        }\n      }\n      this._activeFrameId = frameId;\n    }\n  }\n\n  get samplers() {\n    return this._material._samplerDictionary;\n  }\n\n  get uniforms() {\n    return this._material._uniform_dictionary;\n  }\n\n  waitForComplete() {\n    if (!this._promise) {\n      if (!this._material) {\n        return Promise.reject('RenderPrimitive does not have a material');\n      }\n\n      let completionPromises = [];\n\n      for (let attributeBuffer of this._attributeBuffers) {\n        if (!attributeBuffer._buffer._buffer) {\n          completionPromises.push(attributeBuffer._buffer._promise);\n        }\n      }\n\n      if (this._indexBuffer && !this._indexBuffer._buffer) {\n        completionPromises.push(this._indexBuffer._promise);\n      }\n\n      this._promise = Promise.all(completionPromises).then(() => {\n        this._complete = true;\n        return this;\n      });\n    }\n    return this._promise;\n  }\n}\n\nexport class RenderTexture {\n  constructor(texture) {\n    this._texture = texture;\n    this._complete = false;\n    this._activeFrameId = 0;\n    this._activeCallback = null;\n  }\n\n  markActive(frameId) {\n    if (this._activeCallback && this._activeFrameId != frameId) {\n      this._activeFrameId = frameId;\n      this._activeCallback(this);\n    }\n  }\n}\n\nconst inverseMatrix = mat4.create();\n\nfunction setCap(gl, glEnum, cap, prevState, state) {\n  let change = (state & cap) - (prevState & cap);\n  if (!change) {\n    return;\n  }\n\n  if (change > 0) {\n    gl.enable(glEnum);\n  } else {\n    gl.disable(glEnum);\n  }\n}\n\nclass RenderMaterialSampler {\n  constructor(renderer, materialSampler, index) {\n    this._renderer = renderer;\n    this._uniformName = materialSampler._uniformName;\n    this._renderTexture = renderer._getRenderTexture(materialSampler._texture);\n    this._index = index;\n  }\n\n  set texture(value) {\n    this._renderTexture = this._renderer._getRenderTexture(value);\n  }\n}\n\nclass RenderMaterialUniform {\n  constructor(materialUniform) {\n    this._uniformName = materialUniform._uniformName;\n    this._uniform = null;\n    this._length = materialUniform._length;\n    if (materialUniform._value instanceof Array) {\n      this._value = new Float32Array(materialUniform._value);\n    } else {\n      this._value = new Float32Array([materialUniform._value]);\n    }\n  }\n\n  set value(value) {\n    if (this._value.length == 1) {\n      this._value[0] = value;\n    } else {\n      for (let i = 0; i < this._value.length; ++i) {\n        this._value[i] = value[i];\n      }\n    }\n  }\n}\n\nclass RenderMaterial {\n  constructor(renderer, material, program) {\n    this._program = program;\n    this._state = material.state._state;\n    this._activeFrameId = 0;\n    this._completeForActiveFrame = false;\n\n    this._samplerDictionary = {};\n    this._samplers = [];\n    for (let i = 0; i < material._samplers.length; ++i) {\n      let renderSampler = new RenderMaterialSampler(renderer, material._samplers[i], i);\n      this._samplers.push(renderSampler);\n      this._samplerDictionary[renderSampler._uniformName] = renderSampler;\n    }\n\n    this._uniform_dictionary = {};\n    this._uniforms = [];\n    for (let uniform of material._uniforms) {\n      let renderUniform = new RenderMaterialUniform(uniform);\n      this._uniforms.push(renderUniform);\n      this._uniform_dictionary[renderUniform._uniformName] = renderUniform;\n    }\n\n    this._firstBind = true;\n\n    this._renderOrder = material.renderOrder;\n    if (this._renderOrder == RENDER_ORDER.DEFAULT) {\n      if (this._state & CAP.BLEND) {\n        this._renderOrder = RENDER_ORDER.TRANSPARENT;\n      } else {\n        this._renderOrder = RENDER_ORDER.OPAQUE;\n      }\n    }\n  }\n\n  bind(gl) {\n    // First time we do a binding, cache the uniform locations and remove\n    // unused uniforms from the list.\n    if (this._firstBind) {\n      for (let i = 0; i < this._samplers.length;) {\n        let sampler = this._samplers[i];\n        if (!this._program.uniform[sampler._uniformName]) {\n          this._samplers.splice(i, 1);\n          continue;\n        }\n        ++i;\n      }\n\n      for (let i = 0; i < this._uniforms.length;) {\n        let uniform = this._uniforms[i];\n        uniform._uniform = this._program.uniform[uniform._uniformName];\n        if (!uniform._uniform) {\n          this._uniforms.splice(i, 1);\n          continue;\n        }\n        ++i;\n      }\n      this._firstBind = false;\n    }\n\n    for (let sampler of this._samplers) {\n      gl.activeTexture(gl.TEXTURE0 + sampler._index);\n      if (sampler._renderTexture && sampler._renderTexture._complete) {\n        gl.bindTexture(gl.TEXTURE_2D, sampler._renderTexture._texture);\n      } else {\n        gl.bindTexture(gl.TEXTURE_2D, null);\n      }\n    }\n\n    for (let uniform of this._uniforms) {\n      switch (uniform._length) {\n        case 1: gl.uniform1fv(uniform._uniform, uniform._value); break;\n        case 2: gl.uniform2fv(uniform._uniform, uniform._value); break;\n        case 3: gl.uniform3fv(uniform._uniform, uniform._value); break;\n        case 4: gl.uniform4fv(uniform._uniform, uniform._value); break;\n      }\n    }\n  }\n\n  markActive(frameId) {\n    if (this._activeFrameId != frameId) {\n      this._activeFrameId = frameId;\n      this._completeForActiveFrame = true;\n      for (let i = 0; i < this._samplers.length; ++i) {\n        let sampler = this._samplers[i];\n        if (sampler._renderTexture) {\n          if (!sampler._renderTexture._complete) {\n            this._completeForActiveFrame = false;\n            break;\n          }\n          sampler._renderTexture.markActive(frameId);\n        }\n      }\n    }\n    return this._completeForActiveFrame;\n  }\n\n  // Material State fetchers\n  get cullFace() {\n    return !!(this._state & CAP.CULL_FACE);\n  }\n  get blend() {\n    return !!(this._state & CAP.BLEND);\n  }\n  get depthTest() {\n    return !!(this._state & CAP.DEPTH_TEST);\n  }\n  get stencilTest() {\n    return !!(this._state & CAP.STENCIL_TEST);\n  }\n  get colorMask() {\n    return !!(this._state & CAP.COLOR_MASK);\n  }\n  get depthMask() {\n    return !!(this._state & CAP.DEPTH_MASK);\n  }\n  get stencilMask() {\n    return !!(this._state & CAP.STENCIL_MASK);\n  }\n  get depthFunc() {\n    return ((this._state & MAT_STATE.DEPTH_FUNC_RANGE) >> MAT_STATE.DEPTH_FUNC_SHIFT) + GL.NEVER;\n  }\n  get blendFuncSrc() {\n    return stateToBlendFunc(this._state, MAT_STATE.BLEND_SRC_RANGE, MAT_STATE.BLEND_SRC_SHIFT);\n  }\n  get blendFuncDst() {\n    return stateToBlendFunc(this._state, MAT_STATE.BLEND_DST_RANGE, MAT_STATE.BLEND_DST_SHIFT);\n  }\n\n  // Only really for use from the renderer\n  _capsDiff(otherState) {\n    return (otherState & MAT_STATE.CAPS_RANGE) ^ (this._state & MAT_STATE.CAPS_RANGE);\n  }\n\n  _blendDiff(otherState) {\n    if (!(this._state & CAP.BLEND)) {\n      return 0;\n    }\n    return (otherState & MAT_STATE.BLEND_FUNC_RANGE) ^ (this._state & MAT_STATE.BLEND_FUNC_RANGE);\n  }\n\n  _depthFuncDiff(otherState) {\n    if (!(this._state & CAP.DEPTH_TEST)) {\n      return 0;\n    }\n    return (otherState & MAT_STATE.DEPTH_FUNC_RANGE) ^ (this._state & MAT_STATE.DEPTH_FUNC_RANGE);\n  }\n}\n\nexport class Renderer {\n  constructor(gl) {\n    this._gl = gl || createWebGLContext();\n    this._frameId = 0;\n    this._programCache = {};\n    this._textureCache = {};\n    this._renderPrimitives = Array(RENDER_ORDER.DEFAULT);\n    this._cameraPositions = [];\n\n    this._vaoExt = gl.getExtension('OES_vertex_array_object');\n\n    let fragHighPrecision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);\n    this._defaultFragPrecision = fragHighPrecision.precision > 0 ? 'highp' : 'mediump';\n\n    this._depthMaskNeedsReset = false;\n    this._colorMaskNeedsReset = false;\n\n    this._globalLightColor = vec3.clone(DEF_LIGHT_COLOR);\n    this._globalLightDir = vec3.clone(DEF_LIGHT_DIR);\n  }\n\n  get gl() {\n    return this._gl;\n  }\n\n  set globalLightColor(value) {\n    vec3.copy(this._globalLightColor, value);\n  }\n\n  get globalLightColor() {\n    return vec3.clone(this._globalLightColor);\n  }\n\n  set globalLightDir(value) {\n    vec3.copy(this._globalLightDir, value);\n  }\n\n  get globalLightDir() {\n    return vec3.clone(this._globalLightDir);\n  }\n\n  createRenderBuffer(target, data, usage = GL.STATIC_DRAW) {\n    let gl = this._gl;\n    let glBuffer = gl.createBuffer();\n\n    if (data instanceof Promise) {\n      let renderBuffer = new RenderBuffer(target, usage, data.then((data) => {\n        gl.bindBuffer(target, glBuffer);\n        gl.bufferData(target, data, usage);\n        renderBuffer._length = data.byteLength;\n        return glBuffer;\n      }));\n      return renderBuffer;\n    } else {\n      gl.bindBuffer(target, glBuffer);\n      gl.bufferData(target, data, usage);\n      return new RenderBuffer(target, usage, glBuffer, data.byteLength);\n    }\n  }\n\n  updateRenderBuffer(buffer, data, offset = 0) {\n    if (buffer._buffer) {\n      let gl = this._gl;\n      gl.bindBuffer(buffer._target, buffer._buffer);\n      if (offset == 0 && buffer._length == data.byteLength) {\n        gl.bufferData(buffer._target, data, buffer._usage);\n      } else {\n        gl.bufferSubData(buffer._target, offset, data);\n      }\n    } else {\n      buffer.waitForComplete().then((buffer) => {\n        this.updateRenderBuffer(buffer, data, offset);\n      });\n    }\n  }\n\n  createRenderPrimitive(primitive, material) {\n    let renderPrimitive = new RenderPrimitive(primitive);\n\n    let program = this._getMaterialProgram(material, renderPrimitive);\n    let renderMaterial = new RenderMaterial(this, material, program);\n    renderPrimitive.setRenderMaterial(renderMaterial);\n\n    if (!this._renderPrimitives[renderMaterial._renderOrder]) {\n      this._renderPrimitives[renderMaterial._renderOrder] = [];\n    }\n\n    this._renderPrimitives[renderMaterial._renderOrder].push(renderPrimitive);\n\n    return renderPrimitive;\n  }\n\n  createMesh(primitive, material) {\n    let meshNode = new Node();\n    meshNode.addRenderPrimitive(this.createRenderPrimitive(primitive, material));\n    return meshNode;\n  }\n\n  drawViews(views, rootNode) {\n    if (!rootNode) {\n      return;\n    }\n\n    let gl = this._gl;\n    this._frameId++;\n\n    rootNode.markActive(this._frameId);\n\n    // If there's only one view then flip the algorithm a bit so that we're only\n    // setting the viewport once.\n    if (views.length == 1 && views[0].viewport) {\n      let vp = views[0].viewport;\n      this._gl.viewport(vp.x, vp.y, vp.width, vp.height);\n    }\n\n    // Get the positions of the 'camera' for each view matrix.\n    for (let i = 0; i < views.length; ++i) {\n      mat4.invert(inverseMatrix, views[i].viewMatrix);\n\n      if (this._cameraPositions.length <= i) {\n        this._cameraPositions.push(vec3.create());\n      }\n      let cameraPosition = this._cameraPositions[i];\n      vec3.set(cameraPosition, 0, 0, 0);\n      vec3.transformMat4(cameraPosition, cameraPosition, inverseMatrix);\n    }\n\n    // Draw each set of render primitives in order\n    for (let renderPrimitives of this._renderPrimitives) {\n      if (renderPrimitives && renderPrimitives.length) {\n        this._drawRenderPrimitiveSet(views, renderPrimitives);\n      }\n    }\n\n    if (this._vaoExt) {\n      this._vaoExt.bindVertexArrayOES(null);\n    }\n\n    if (this._depthMaskNeedsReset) {\n      gl.depthMask(true);\n    }\n    if (this._colorMaskNeedsReset) {\n      gl.colorMask(true, true, true, true);\n    }\n  }\n\n  _drawRenderPrimitiveSet(views, renderPrimitives) {\n    let gl = this._gl;\n    let program = null;\n    let material = null;\n    let attribMask = 0;\n\n    // Loop through every primitive known to the renderer.\n    for (let primitive of renderPrimitives) {\n      // Skip over those that haven't been marked as active for this frame.\n      if (primitive._activeFrameId != this._frameId) {\n        continue;\n      }\n\n      // Bind the primitive material's program if it's different than the one we\n      // were using for the previous primitive.\n      // TODO: The ording of this could be more efficient.\n      if (program != primitive._material._program) {\n        program = primitive._material._program;\n        program.use();\n\n        if (program.uniform.LIGHT_DIRECTION) {\n          gl.uniform3fv(program.uniform.LIGHT_DIRECTION, this._globalLightDir);\n        }\n\n        if (program.uniform.LIGHT_COLOR) {\n          gl.uniform3fv(program.uniform.LIGHT_COLOR, this._globalLightColor);\n        }\n\n        if (views.length == 1) {\n          gl.uniformMatrix4fv(program.uniform.PROJECTION_MATRIX, false, views[0].projectionMatrix);\n          gl.uniformMatrix4fv(program.uniform.VIEW_MATRIX, false, views[0].viewMatrix);\n          gl.uniform3fv(program.uniform.CAMERA_POSITION, this._cameraPositions[0]);\n          gl.uniform1i(program.uniform.EYE_INDEX, views[0].eyeIndex);\n        }\n      }\n\n      if (material != primitive._material) {\n        this._bindMaterialState(primitive._material, material);\n        primitive._material.bind(gl, program, material);\n        material = primitive._material;\n      }\n\n      if (this._vaoExt) {\n        if (primitive._vao) {\n          this._vaoExt.bindVertexArrayOES(primitive._vao);\n        } else {\n          primitive._vao = this._vaoExt.createVertexArrayOES();\n          this._vaoExt.bindVertexArrayOES(primitive._vao);\n          this._bindPrimitive(primitive);\n        }\n      } else {\n        this._bindPrimitive(primitive, attribMask);\n        attribMask = primitive._attributeMask;\n      }\n\n      for (let i = 0; i < views.length; ++i) {\n        let view = views[i];\n        if (views.length > 1) {\n          if (view.viewport) {\n            let vp = view.viewport;\n            gl.viewport(vp.x, vp.y, vp.width, vp.height);\n          }\n          gl.uniformMatrix4fv(program.uniform.PROJECTION_MATRIX, false, view.projectionMatrix);\n          gl.uniformMatrix4fv(program.uniform.VIEW_MATRIX, false, view.viewMatrix);\n          gl.uniform3fv(program.uniform.CAMERA_POSITION, this._cameraPositions[i]);\n          gl.uniform1i(program.uniform.EYE_INDEX, view.eyeIndex);\n        }\n\n        for (let instance of primitive._instances) {\n          if (instance._activeFrameId != this._frameId) {\n            continue;\n          }\n\n          gl.uniformMatrix4fv(program.uniform.MODEL_MATRIX, false, instance.worldMatrix);\n\n          if (primitive._indexBuffer) {\n            gl.drawElements(primitive._mode, primitive._elementCount,\n                primitive._indexType, primitive._indexByteOffset);\n          } else {\n            gl.drawArrays(primitive._mode, 0, primitive._elementCount);\n          }\n        }\n      }\n    }\n  }\n\n  _getRenderTexture(texture) {\n    if (!texture) {\n      return null;\n    }\n\n    let key = texture.textureKey;\n    if (!key) {\n      throw new Error('Texure does not have a valid key');\n    }\n\n    if (key in this._textureCache) {\n      return this._textureCache[key];\n    } else {\n      let gl = this._gl;\n      let textureHandle = gl.createTexture();\n\n      let renderTexture = new RenderTexture(textureHandle);\n      this._textureCache[key] = renderTexture;\n\n      if (texture instanceof DataTexture) {\n        gl.bindTexture(gl.TEXTURE_2D, textureHandle);\n        gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.width, texture.height,\n                                     0, texture.format, texture._type, texture._data);\n        this._setSamplerParameters(texture);\n        renderTexture._complete = true;\n      } else {\n        texture.waitForComplete().then(() => {\n          gl.bindTexture(gl.TEXTURE_2D, textureHandle);\n          gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.format, gl.UNSIGNED_BYTE, texture.source);\n          this._setSamplerParameters(texture);\n          renderTexture._complete = true;\n\n          if (texture instanceof VideoTexture) {\n            // Once the video starts playing, set a callback to update it's\n            // contents each frame.\n            texture._video.addEventListener('playing', () => {\n              renderTexture._activeCallback = () => {\n                if (!texture._video.paused && !texture._video.waiting) {\n                  gl.bindTexture(gl.TEXTURE_2D, textureHandle);\n                  gl.texImage2D(gl.TEXTURE_2D, 0, texture.format, texture.format, gl.UNSIGNED_BYTE, texture.source);\n                }\n              };\n            });\n          }\n        });\n      }\n\n      return renderTexture;\n    }\n  }\n\n  _setSamplerParameters(texture) {\n    let gl = this._gl;\n\n    let sampler = texture.sampler;\n    let powerOfTwo = isPowerOfTwo(texture.width) && isPowerOfTwo(texture.height);\n    let mipmap = powerOfTwo && texture.mipmap;\n    if (mipmap) {\n      gl.generateMipmap(gl.TEXTURE_2D);\n    }\n\n    let minFilter = sampler.minFilter || (mipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);\n    let wrapS = sampler.wrapS || (powerOfTwo ? gl.REPEAT : gl.CLAMP_TO_EDGE);\n    let wrapT = sampler.wrapT || (powerOfTwo ? gl.REPEAT : gl.CLAMP_TO_EDGE);\n\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, sampler.magFilter || gl.LINEAR);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);\n  }\n\n  _getProgramKey(name, defines) {\n    let key = `${name}:`;\n\n    for (let define in defines) {\n      key += `${define}=${defines[define]},`;\n    }\n\n    return key;\n  }\n\n  _getMaterialProgram(material, renderPrimitive) {\n    let materialName = material.materialName;\n    let vertexSource = material.vertexSource;\n    let fragmentSource = material.fragmentSource;\n\n    // These should always be defined for every material\n    if (materialName == null) {\n      throw new Error('Material does not have a name');\n    }\n    if (vertexSource == null) {\n      throw new Error(`Material \"${materialName}\" does not have a vertex source`);\n    }\n    if (fragmentSource == null) {\n      throw new Error(`Material \"${materialName}\" does not have a fragment source`);\n    }\n\n    let defines = material.getProgramDefines(renderPrimitive);\n    let key = this._getProgramKey(materialName, defines);\n\n    if (key in this._programCache) {\n      return this._programCache[key];\n    } else {\n      let multiview = false; // Handle this dynamically later\n      let fullVertexSource = vertexSource;\n      fullVertexSource += multiview ? VERTEX_SHADER_MULTI_ENTRY :\n                                      VERTEX_SHADER_SINGLE_ENTRY;\n\n      let precisionMatch = fragmentSource.match(PRECISION_REGEX);\n      let fragPrecisionHeader = precisionMatch ? '' : `precision ${this._defaultFragPrecision} float;\\n`;\n\n      let fullFragmentSource = fragPrecisionHeader + fragmentSource;\n      fullFragmentSource += FRAGMENT_SHADER_ENTRY;\n\n      let program = new Program(this._gl, fullVertexSource, fullFragmentSource, ATTRIB, defines);\n      this._programCache[key] = program;\n\n      program.onNextUse((program) => {\n        // Bind the samplers to the right texture index. This is constant for\n        // the lifetime of the program.\n        for (let i = 0; i < material._samplers.length; ++i) {\n          let sampler = material._samplers[i];\n          let uniform = program.uniform[sampler._uniformName];\n          if (uniform) {\n            this._gl.uniform1i(uniform, i);\n          }\n        }\n      });\n\n      return program;\n    }\n  }\n\n  _bindPrimitive(primitive, attribMask) {\n    let gl = this._gl;\n\n    // If the active attributes have changed then update the active set.\n    if (attribMask != primitive._attributeMask) {\n      for (let attrib in ATTRIB) {\n        if (primitive._attributeMask & ATTRIB_MASK[attrib]) {\n          gl.enableVertexAttribArray(ATTRIB[attrib]);\n        } else {\n          gl.disableVertexAttribArray(ATTRIB[attrib]);\n        }\n      }\n    }\n\n    // Bind the primitive attributes and indices.\n    for (let attributeBuffer of primitive._attributeBuffers) {\n      gl.bindBuffer(gl.ARRAY_BUFFER, attributeBuffer._buffer._buffer);\n      for (let attrib of attributeBuffer._attributes) {\n        gl.vertexAttribPointer(\n            attrib._attrib_index, attrib._componentCount, attrib._componentType,\n            attrib._normalized, attrib._stride, attrib._byteOffset);\n      }\n    }\n\n    if (primitive._indexBuffer) {\n      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, primitive._indexBuffer._buffer);\n    } else {\n      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n    }\n  }\n\n  _bindMaterialState(material, prevMaterial = null) {\n    let gl = this._gl;\n\n    let state = material._state;\n    let prevState = prevMaterial ? prevMaterial._state : ~state;\n\n    // Return early if both materials use identical state\n    if (state == prevState) {\n      return;\n    }\n\n    // Any caps bits changed?\n    if (material._capsDiff(prevState)) {\n      setCap(gl, gl.CULL_FACE, CAP.CULL_FACE, prevState, state);\n      setCap(gl, gl.BLEND, CAP.BLEND, prevState, state);\n      setCap(gl, gl.DEPTH_TEST, CAP.DEPTH_TEST, prevState, state);\n      setCap(gl, gl.STENCIL_TEST, CAP.STENCIL_TEST, prevState, state);\n\n      let colorMaskChange = (state & CAP.COLOR_MASK) - (prevState & CAP.COLOR_MASK);\n      if (colorMaskChange) {\n        let mask = colorMaskChange > 1;\n        this._colorMaskNeedsReset = !mask;\n        gl.colorMask(mask, mask, mask, mask);\n      }\n\n      let depthMaskChange = (state & CAP.DEPTH_MASK) - (prevState & CAP.DEPTH_MASK);\n      if (depthMaskChange) {\n        this._depthMaskNeedsReset = !(depthMaskChange > 1);\n        gl.depthMask(depthMaskChange > 1);\n      }\n\n      let stencilMaskChange = (state & CAP.STENCIL_MASK) - (prevState & CAP.STENCIL_MASK);\n      if (stencilMaskChange) {\n        gl.stencilMask(stencilMaskChange > 1);\n      }\n    }\n\n    // Blending enabled and blend func changed?\n    if (material._blendDiff(prevState)) {\n      gl.blendFunc(material.blendFuncSrc, material.blendFuncDst);\n    }\n\n    // Depth testing enabled and depth func changed?\n    if (material._depthFuncDiff(prevState)) {\n      gl.depthFunc(material.depthFunc);\n    }\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nconst GL = WebGLRenderingContext; // For enums\n\nexport class TextureSampler {\n  constructor() {\n    this.minFilter = null;\n    this.magFilter = null;\n    this.wrapS = null;\n    this.wrapT = null;\n  }\n}\n\nexport class Texture {\n  constructor() {\n    this.sampler = new TextureSampler();\n    this.mipmap = true;\n    // TODO: Anisotropy\n  }\n\n  get format() {\n    return GL.RGBA;\n  }\n\n  get width() {\n    return 0;\n  }\n\n  get height() {\n    return 0;\n  }\n\n  get textureKey() {\n    return null;\n  }\n}\n\nexport class ImageTexture extends Texture {\n  constructor(img) {\n    super();\n\n    this._img = img;\n    this._imgBitmap = null;\n\n    if (img.src && img.complete) {\n      if (img.naturalWidth) {\n        this._promise = this._finishImage();\n      } else {\n        this._promise = Promise.reject('Image provided had failed to load.');\n      }\n    } else {\n      this._promise = new Promise((resolve, reject) => {\n        img.addEventListener('load', () => resolve(this._finishImage()));\n        img.addEventListener('error', reject);\n      });\n    }\n  }\n\n  _finishImage() {\n    if (window.createImageBitmap) {\n      return window.createImageBitmap(this._img).then((imgBitmap) => {\n        this._imgBitmap = imgBitmap;\n        return Promise.resolve(this);\n      });\n    }\n    return Promise.resolve(this);\n  }\n\n  get format() {\n    // TODO: Can be RGB in some cases.\n    return GL.RGBA;\n  }\n\n  get width() {\n    return this._img.width;\n  }\n\n  get height() {\n    return this._img.height;\n  }\n\n  waitForComplete() {\n    return this._promise;\n  }\n\n  get textureKey() {\n    return this._img.src;\n  }\n\n  get source() {\n    return this._imgBitmap || this._img;\n  }\n}\n\nexport class UrlTexture extends ImageTexture {\n  constructor(url) {\n    let img = new Image();\n    super(img);\n    img.src = url;\n  }\n}\n\nexport class BlobTexture extends ImageTexture {\n  constructor(blob) {\n    let img = new Image();\n    super(img);\n    img.src = window.URL.createObjectURL(blob);\n  }\n}\n\nexport class VideoTexture extends Texture {\n  constructor(video) {\n    super();\n\n    this._video = video;\n\n    if (video.readyState >= 2) {\n      this._promise = Promise.resolve(this);\n    } else if (video.error) {\n      this._promise = Promise.reject(video.error);\n    } else {\n      this._promise = new Promise((resolve, reject) => {\n        video.addEventListener('loadeddata', () => resolve(this));\n        video.addEventListener('error', reject);\n      });\n    }\n  }\n\n  get format() {\n    // TODO: Can be RGB in some cases.\n    return GL.RGBA;\n  }\n\n  get width() {\n    return this._video.videoWidth;\n  }\n\n  get height() {\n    return this._video.videoHeight;\n  }\n\n  waitForComplete() {\n    return this._promise;\n  }\n\n  get textureKey() {\n    return this._video.src;\n  }\n\n  get source() {\n    return this._video;\n  }\n}\n\nlet nextDataTextureIndex = 0;\n\nexport class DataTexture extends Texture {\n  constructor(data, width, height, format = GL.RGBA, type = GL.UNSIGNED_BYTE) {\n    super();\n\n    this._data = data;\n    this._width = width;\n    this._height = height;\n    this._format = format;\n    this._type = type;\n    this._key = `DATA_${nextDataTextureIndex}`;\n    nextDataTextureIndex++;\n  }\n\n  get format() {\n    return this._format;\n  }\n\n  get width() {\n    return this._width;\n  }\n\n  get height() {\n    return this._height;\n  }\n\n  get textureKey() {\n    return this._key;\n  }\n}\n\nexport class ColorTexture extends DataTexture {\n  constructor(r, g, b, a) {\n    let colorData = new Uint8Array([r*255.0, g*255.0, b*255.0, a*255.0]);\n    super(colorData, 1, 1);\n\n    this.mipmap = false;\n    this._key = `COLOR_${colorData[0]}_${colorData[1]}_${colorData[2]}_${colorData[3]}`;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nexport {Node} from './core/node.js';\nexport {Renderer, createWebGLContext} from './core/renderer.js';\nexport {UrlTexture} from './core/texture.js';\n\nexport {PrimitiveStream} from './geometry/primitive-stream.js';\nexport {BoxBuilder} from './geometry/box-builder.js';\n\nexport {PbrMaterial} from './materials/pbr.js';\n\nexport {mat4, mat3, vec3, vec4, quat} from './math/gl-matrix.js';\n\nexport {BoundsRenderer} from './nodes/bounds-renderer.js';\nexport {ButtonNode} from './nodes/button.js';\nexport {DropShadowNode} from './nodes/drop-shadow.js';\nexport {CubeSeaNode} from './nodes/cube-sea.js';\nexport {Gltf2Node} from './nodes/gltf2.js';\nexport {SkyboxNode} from './nodes/skybox.js';\nexport {VideoNode} from './nodes/video.js';\n\nexport {WebXRView, Scene} from './scenes/scene.js';\n\nexport {FallbackHelper} from './util/fallback-helper.js';\nexport {QueryArgs} from './util/query-args.js';\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {GeometryBuilderBase} from './primitive-stream.js';\n\nexport class BoxBuilder extends GeometryBuilderBase {\n  pushBox(min, max) {\n    let stream = this.primitiveStream;\n\n    let w = max[0] - min[0];\n    let h = max[1] - min[1];\n    let d = max[2] - min[2];\n\n    let wh = w * 0.5;\n    let hh = h * 0.5;\n    let dh = d * 0.5;\n\n    let cx = min[0] + wh;\n    let cy = min[1] + hh;\n    let cz = min[2] + dh;\n\n    stream.startGeometry();\n\n    // Bottom\n    let idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+1, idx+2);\n    stream.pushTriangle(idx, idx+2, idx+3);\n\n    //                 X       Y       Z      U    V    NX    NY   NZ\n    stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, 0.0, -1.0, 0.0);\n    stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 0.0, -1.0, 0.0);\n    stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 1.0, 0.0, 0.0, -1.0, 0.0);\n    stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 0.0, 0.0, 0.0, -1.0, 0.0);\n\n    // Top\n    idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+2, idx+1);\n    stream.pushTriangle(idx, idx+3, idx+2);\n\n    stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, 0.0, 1.0, 0.0);\n    stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 0.0, 1.0, 0.0);\n    stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 1.0, 1.0, 0.0, 1.0, 0.0);\n    stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 0.0, 1.0, 0.0, 1.0, 0.0);\n\n    // Left\n    idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+2, idx+1);\n    stream.pushTriangle(idx, idx+3, idx+2);\n\n    stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, -1.0, 0.0, 0.0);\n    stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, -1.0, 0.0, 0.0);\n    stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 1.0, 0.0, -1.0, 0.0, 0.0);\n    stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 1.0, 1.0, -1.0, 0.0, 0.0);\n\n    // Right\n    idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+1, idx+2);\n    stream.pushTriangle(idx, idx+2, idx+3);\n\n    stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 1.0, 0.0, 0.0);\n    stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 1.0, 0.0, 0.0);\n    stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 0.0, 0.0, 1.0, 0.0, 0.0);\n    stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 0.0, 1.0, 1.0, 0.0, 0.0);\n\n    // Back\n    idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+2, idx+1);\n    stream.pushTriangle(idx, idx+3, idx+2);\n\n    stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 0.0, 0.0, -1.0);\n    stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, 0.0, 0.0, -1.0);\n    stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, 0.0, 0.0, -1.0);\n    stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 0.0, 0.0, -1.0);\n\n    // Front\n    idx = stream.nextVertexIndex;\n    stream.pushTriangle(idx, idx+1, idx+2);\n    stream.pushTriangle(idx, idx+2, idx+3);\n\n    stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 0.0, 1.0, 0.0, 0.0, 1.0);\n    stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 1.0, 1.0, 0.0, 0.0, 1.0);\n    stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 1.0, 0.0, 0.0, 0.0, 1.0);\n    stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 0.0, 0.0, 0.0, 0.0, 1.0);\n\n    stream.endGeometry();\n  }\n\n  pushCube(center = [0, 0, 0], size = 1.0) {\n    let hs = size * 0.5;\n    this.pushBox([center[0] - hs, center[1] - hs, center[2] - hs],\n                 [center[0] + hs, center[1] + hs, center[2] + hs]);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {mat3, vec3} from '../math/gl-matrix.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nconst tempVec3 = vec3.create();\n\nexport class PrimitiveStream {\n  constructor(options) {\n    this._vertices = [];\n    this._indices = [];\n\n    this._geometryStarted = false;\n\n    this._vertexOffset = 0;\n    this._vertexIndex = 0;\n    this._highIndex = 0;\n\n    this._flipWinding = false;\n    this._invertNormals = false;\n    this._transform = null;\n    this._normalTransform = null;\n    this._min = null;\n    this._max = null;\n  }\n\n  set flipWinding(value) {\n    if (this._geometryStarted) {\n      throw new Error(`Cannot change flipWinding before ending the current geometry.`);\n    }\n    this._flipWinding = value;\n  }\n\n  get flipWinding() {\n    this._flipWinding;\n  }\n\n  set invertNormals(value) {\n    if (this._geometryStarted) {\n      throw new Error(`Cannot change invertNormals before ending the current geometry.`);\n    }\n    this._invertNormals = value;\n  }\n\n  get invertNormals() {\n    this._invertNormals;\n  }\n\n  set transform(value) {\n    if (this._geometryStarted) {\n      throw new Error(`Cannot change transform before ending the current geometry.`);\n    }\n    this._transform = value;\n    if (this._transform) {\n      if (!this._normalTransform) {\n        this._normalTransform = mat3.create();\n      }\n      mat3.fromMat4(this._normalTransform, this._transform);\n    }\n  }\n\n  get transform() {\n    this._transform;\n  }\n\n  startGeometry() {\n    if (this._geometryStarted) {\n      throw new Error(`Attempted to start a new geometry before the previous one was ended.`);\n    }\n\n    this._geometryStarted = true;\n    this._vertexIndex = 0;\n    this._highIndex = 0;\n  }\n\n  endGeometry() {\n    if (!this._geometryStarted) {\n      throw new Error(`Attempted to end a geometry before one was started.`);\n    }\n\n    if (this._highIndex >= this._vertexIndex) {\n      throw new Error(`Geometry contains indices that are out of bounds.\n                       (Contains an index of ${this._highIndex} when the vertex count is ${this._vertexIndex})`);\n    }\n\n    this._geometryStarted = false;\n    this._vertexOffset += this._vertexIndex;\n\n    // TODO: Anything else need to be done to finish processing here?\n  }\n\n  pushVertex(x, y, z, u = 0, v = 0, nx = 0, ny = 0, nz = 1) {\n    if (!this._geometryStarted) {\n      throw new Error(`Cannot push vertices before calling startGeometry().`);\n    }\n\n    // Transform the incoming vertex if we have a transformation matrix\n    if (this._transform) {\n      tempVec3[0] = x;\n      tempVec3[1] = y;\n      tempVec3[2] = z;\n      vec3.transformMat4(tempVec3, tempVec3, this._transform);\n      x = tempVec3[0];\n      y = tempVec3[1];\n      z = tempVec3[2];\n\n      tempVec3[0] = nx;\n      tempVec3[1] = ny;\n      tempVec3[2] = nz;\n      vec3.transformMat3(tempVec3, tempVec3, this._normalTransform);\n      nx = tempVec3[0];\n      ny = tempVec3[1];\n      nz = tempVec3[2];\n    }\n\n    if (this._invertNormals) {\n      nx *= -1.0;\n      ny *= -1.0;\n      nz *= -1.0;\n    }\n\n    this._vertices.push(x, y, z, u, v, nx, ny, nz);\n\n    if (this._min) {\n      this._min[0] = Math.min(this._min[0], x);\n      this._min[1] = Math.min(this._min[1], y);\n      this._min[2] = Math.min(this._min[2], z);\n      this._max[0] = Math.max(this._max[0], x);\n      this._max[1] = Math.max(this._max[1], y);\n      this._max[2] = Math.max(this._max[2], z);\n    } else {\n      this._min = vec3.fromValues(x, y, z);\n      this._max = vec3.fromValues(x, y, z);\n    }\n\n    return this._vertexIndex++;\n  }\n\n  get nextVertexIndex() {\n    return this._vertexIndex;\n  }\n\n  pushTriangle(idxA, idxB, idxC) {\n    if (!this._geometryStarted) {\n      throw new Error(`Cannot push triangles before calling startGeometry().`);\n    }\n\n    this._highIndex = Math.max(this._highIndex, idxA, idxB, idxC);\n\n    idxA += this._vertexOffset;\n    idxB += this._vertexOffset;\n    idxC += this._vertexOffset;\n\n    if (this._flipWinding) {\n      this._indices.push(idxC, idxB, idxA);\n    } else {\n      this._indices.push(idxA, idxB, idxC);\n    }\n  }\n\n  clear() {\n    if (this._geometryStarted) {\n      throw new Error(`Cannot clear before ending the current geometry.`);\n    }\n\n    this._vertices = [];\n    this._indices = [];\n    this._vertexOffset = 0;\n    this._min = null;\n    this._max = null;\n  }\n\n  finishPrimitive(renderer) {\n    if (!this._vertexOffset) {\n      throw new Error(`Attempted to call finishPrimitive() before creating any geometry.`);\n    }\n\n    let vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(this._vertices));\n    let indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(this._indices));\n\n    let attribs = [\n      new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 32, 0),\n      new PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 32, 12),\n      new PrimitiveAttribute('NORMAL', vertexBuffer, 3, GL.FLOAT, 32, 20),\n    ];\n\n    let primitive = new Primitive(attribs, this._indices.length);\n    primitive.setIndexBuffer(indexBuffer);\n    primitive.setBounds(this._min, this._max);\n\n    return primitive;\n  }\n}\n\nexport class GeometryBuilderBase {\n  constructor(primitiveStream) {\n    if (primitiveStream) {\n      this._stream = primitiveStream;\n    } else {\n      this._stream = new PrimitiveStream();\n    }\n  }\n\n  set primitiveStream(value) {\n    this._stream = value;\n  }\n\n  get primitiveStream() {\n    return this._stream;\n  }\n\n  finishPrimitive(renderer) {\n    return this._stream.finishPrimitive(renderer);\n  }\n\n  clear() {\n    this._stream.clear();\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {PbrMaterial} from '../materials/pbr.js';\nimport {Node} from '../core/node.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {ImageTexture, ColorTexture} from '../core/texture.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nconst GLB_MAGIC = 0x46546C67;\nconst CHUNK_TYPE = {\n  JSON: 0x4E4F534A,\n  BIN: 0x004E4942,\n};\n\nfunction isAbsoluteUri(uri) {\n  let absRegEx = new RegExp('^'+window.location.protocol, 'i');\n  return !!uri.match(absRegEx);\n}\n\nfunction isDataUri(uri) {\n  let dataRegEx = /^data:/;\n  return !!uri.match(dataRegEx);\n}\n\nfunction resolveUri(uri, baseUrl) {\n  if (isAbsoluteUri(uri) || isDataUri(uri)) {\n      return uri;\n  }\n  return baseUrl + uri;\n}\n\nfunction getComponentCount(type) {\n  switch (type) {\n    case 'SCALAR': return 1;\n    case 'VEC2': return 2;\n    case 'VEC3': return 3;\n    case 'VEC4': return 4;\n    default: return 0;\n  }\n}\n\n/**\n * Gltf2SceneLoader\n * Loads glTF 2.0 scenes into a renderable node tree.\n */\n\nexport class Gltf2Loader {\n  constructor(renderer) {\n    this.renderer = renderer;\n    this._gl = renderer._gl;\n  }\n\n  loadFromUrl(url) {\n    return fetch(url)\n        .then((response) => {\n          let i = url.lastIndexOf('/');\n          let baseUrl = (i !== 0) ? url.substring(0, i + 1) : '';\n\n          if (url.endsWith('.gltf')) {\n            return response.json().then((json) => {\n              return this.loadFromJson(json, baseUrl);\n            });\n          } else if (url.endsWith('.glb')) {\n            return response.arrayBuffer().then((arrayBuffer) => {\n              return this.loadFromBinary(arrayBuffer, baseUrl);\n            });\n          } else {\n            throw new Error('Unrecognized file extension');\n          }\n        });\n  }\n\n  loadFromBinary(arrayBuffer, baseUrl) {\n    let headerView = new DataView(arrayBuffer, 0, 12);\n    let magic = headerView.getUint32(0, true);\n    let version = headerView.getUint32(4, true);\n    let length = headerView.getUint32(8, true);\n\n    if (magic != GLB_MAGIC) {\n      throw new Error('Invalid magic string in binary header.');\n    }\n\n    if (version != 2) {\n      throw new Error('Incompatible version in binary header.');\n    }\n\n    let chunks = {};\n    let chunkOffset = 12;\n    while (chunkOffset < length) {\n      let chunkHeaderView = new DataView(arrayBuffer, chunkOffset, 8);\n      let chunkLength = chunkHeaderView.getUint32(0, true);\n      let chunkType = chunkHeaderView.getUint32(4, true);\n      chunks[chunkType] = arrayBuffer.slice(chunkOffset + 8, chunkOffset + 8 + chunkLength);\n      chunkOffset += chunkLength + 8;\n    }\n\n    if (!chunks[CHUNK_TYPE.JSON]) {\n      throw new Error('File contained no json chunk.');\n    }\n\n    let decoder = new TextDecoder('utf-8');\n    let jsonString = decoder.decode(chunks[CHUNK_TYPE.JSON]);\n    let json = JSON.parse(jsonString);\n    return this.loadFromJson(json, baseUrl, chunks[CHUNK_TYPE.BIN]);\n  }\n\n  loadFromJson(json, baseUrl, binaryChunk) {\n    if (!json.asset) {\n      throw new Error('Missing asset description.');\n    }\n\n    if (json.asset.minVersion != '2.0' && json.asset.version != '2.0') {\n      throw new Error('Incompatible asset version.');\n    }\n\n    let buffers = [];\n    if (binaryChunk) {\n      buffers[0] = new Gltf2Resource({}, baseUrl, binaryChunk);\n    } else {\n      for (let buffer of json.buffers) {\n        buffers.push(new Gltf2Resource(buffer, baseUrl));\n      }\n    }\n\n    let bufferViews = [];\n    for (let bufferView of json.bufferViews) {\n      bufferViews.push(new Gltf2BufferView(bufferView, buffers));\n    }\n\n    let images = [];\n    if (json.images) {\n      for (let image of json.images) {\n        images.push(new Gltf2Resource(image, baseUrl));\n      }\n    }\n\n    let textures = [];\n    if (json.textures) {\n      for (let texture of json.textures) {\n        let image = images[texture.source];\n        let glTexture = image.texture(bufferViews);\n        if (texture.sampler) {\n          let sampler = sampler[texture.sampler];\n          glTexture.sampler.minFilter = sampler.minFilter;\n          glTexture.sampler.magFilter = sampler.magFilter;\n          glTexture.sampler.wrapS = sampler.wrapS;\n          glTexture.sampler.wrapT = sampler.wrapT;\n        }\n        textures.push(glTexture);\n      }\n    }\n\n    function getTexture(textureInfo) {\n      if (!textureInfo) {\n        return null;\n      }\n      return textures[textureInfo.index];\n    }\n\n    let materials = [];\n    if (json.materials) {\n      for (let material of json.materials) {\n        let glMaterial = new PbrMaterial();\n        let pbr = material.pbrMetallicRoughness || {};\n\n        glMaterial.baseColorFactor.value = pbr.baseColorFactor || [1, 1, 1, 1];\n        glMaterial.baseColor.texture = getTexture(pbr.baseColorTexture);\n        glMaterial.metallicRoughnessFactor.value = [\n          pbr.metallicFactor || 1.0,\n          pbr.roughnessFactor || 1.0,\n        ];\n        glMaterial.metallicRoughness.texture = getTexture(pbr.metallicRoughnessTexture);\n        glMaterial.normal.texture = getTexture(json.normalTexture);\n        glMaterial.occlusion.texture = getTexture(json.occlusionTexture);\n        glMaterial.occlusionStrength.value = (json.occlusionTexture && json.occlusionTexture.strength) ?\n                                              json.occlusionTexture.strength : 1.0;\n        glMaterial.emissiveFactor.value = material.emissiveFactor || [0, 0, 0];\n        glMaterial.emissive.texture = getTexture(json.emissiveTexture);\n        if (!glMaterial.emissive.texture && json.emissiveFactor) {\n          glMaterial.emissive.texture = new ColorTexture(1.0, 1.0, 1.0, 1.0);\n        }\n\n        switch (material.alphaMode) {\n          case 'BLEND':\n            glMaterial.state.blend = true;\n            break;\n          case 'MASK':\n            // Not really supported.\n            glMaterial.state.blend = true;\n            break;\n          default: // Includes 'OPAQUE'\n            glMaterial.state.blend = false;\n        }\n\n        // glMaterial.alpha_mode = material.alphaMode;\n        // glMaterial.alpha_cutoff = material.alphaCutoff;\n        glMaterial.state.cullFace = !(material.doubleSided);\n\n        materials.push(glMaterial);\n      }\n    }\n\n    let accessors = json.accessors;\n\n    let meshes = [];\n    for (let mesh of json.meshes) {\n      let glMesh = new Gltf2Mesh();\n      meshes.push(glMesh);\n\n      for (let primitive of mesh.primitives) {\n        let material = null;\n        if ('material' in primitive) {\n          material = materials[primitive.material];\n        } else {\n          // Create a \"default\" material if the primitive has none.\n          material = new PbrMaterial();\n        }\n\n        let attributes = [];\n        let elementCount = 0;\n        /* let glPrimitive = new Gltf2Primitive(primitive, material);\n        glMesh.primitives.push(glPrimitive); */\n\n        let min = null;\n        let max = null;\n\n        for (let name in primitive.attributes) {\n          let accessor = accessors[primitive.attributes[name]];\n          let bufferView = bufferViews[accessor.bufferView];\n          elementCount = accessor.count;\n\n          let glAttribute = new PrimitiveAttribute(\n            name,\n            bufferView.renderBuffer(this.renderer, GL.ARRAY_BUFFER),\n            getComponentCount(accessor.type),\n            accessor.componentType,\n            bufferView.byteStride || 0,\n            accessor.byteOffset || 0\n          );\n          glAttribute.normalized = accessor.normalized || false;\n\n          if (name == 'POSITION') {\n            min = accessor.min;\n            max = accessor.max;\n          }\n\n          attributes.push(glAttribute);\n        }\n\n        let glPrimitive = new Primitive(attributes, elementCount, primitive.mode);\n\n        if ('indices' in primitive) {\n          let accessor = accessors[primitive.indices];\n          let bufferView = bufferViews[accessor.bufferView];\n\n          glPrimitive.setIndexBuffer(\n            bufferView.renderBuffer(this.renderer, GL.ELEMENT_ARRAY_BUFFER),\n            accessor.byteOffset || 0,\n            accessor.componentType\n          );\n          glPrimitive.indexType = accessor.componentType;\n          glPrimitive.indexByteOffset = accessor.byteOffset || 0;\n          glPrimitive.elementCount = accessor.count;\n        }\n\n        if (min && max) {\n          glPrimitive.setBounds(min, max);\n        }\n\n        // After all the attributes have been processed, get a program that is\n        // appropriate for both the material and the primitive attributes.\n        glMesh.primitives.push(\n            this.renderer.createRenderPrimitive(glPrimitive, material));\n      }\n    }\n\n    let sceneNode = new Node();\n    let scene = json.scenes[json.scene];\n    for (let nodeId of scene.nodes) {\n      let node = json.nodes[nodeId];\n      sceneNode.addNode(\n          this.processNodes(node, json.nodes, meshes));\n    }\n\n    return sceneNode;\n  }\n\n  processNodes(node, nodes, meshes) {\n    let glNode = new Node();\n    glNode.name = node.name;\n\n    if ('mesh' in node) {\n      let mesh = meshes[node.mesh];\n      for (let primitive of mesh.primitives) {\n        glNode.addRenderPrimitive(primitive);\n      }\n    }\n\n    if (node.matrix) {\n      glNode.matrix = new Float32Array(node.matrix);\n    } else if (node.translation || node.rotation || node.scale) {\n      if (node.translation) {\n        glNode.translation = new Float32Array(node.translation);\n      }\n\n      if (node.rotation) {\n        glNode.rotation = new Float32Array(node.rotation);\n      }\n\n      if (node.scale) {\n        glNode.scale = new Float32Array(node.scale);\n      }\n    }\n\n    if (node.children) {\n      for (let nodeId of node.children) {\n        let node = nodes[nodeId];\n        glNode.addNode(this.processNodes(node, nodes, meshes));\n      }\n    }\n\n    return glNode;\n  }\n}\n\nclass Gltf2Mesh {\n  constructor() {\n    this.primitives = [];\n  }\n}\n\nclass Gltf2BufferView {\n  constructor(json, buffers) {\n    this.buffer = buffers[json.buffer];\n    this.byteOffset = json.byteOffset || 0;\n    this.byteLength = json.byteLength || null;\n    this.byteStride = json.byteStride;\n\n    this._viewPromise = null;\n    this._renderBuffer = null;\n  }\n\n  dataView() {\n    if (!this._viewPromise) {\n      this._viewPromise = this.buffer.arrayBuffer().then((arrayBuffer) => {\n        return new DataView(arrayBuffer, this.byteOffset, this.byteLength);\n      });\n    }\n    return this._viewPromise;\n  }\n\n  renderBuffer(renderer, target) {\n    if (!this._renderBuffer) {\n      this._renderBuffer = renderer.createRenderBuffer(target, this.dataView());\n    }\n    return this._renderBuffer;\n  }\n}\n\nclass Gltf2Resource {\n  constructor(json, baseUrl, arrayBuffer) {\n    this.json = json;\n    this.baseUrl = baseUrl;\n\n    this._dataPromise = null;\n    this._texture = null;\n    if (arrayBuffer) {\n      this._dataPromise = Promise.resolve(arrayBuffer);\n    }\n  }\n\n  arrayBuffer() {\n    if (!this._dataPromise) {\n      if (isDataUri(this.json.uri)) {\n        let base64String = this.json.uri.replace('data:application/octet-stream;base64,', '');\n        let binaryArray = Uint8Array.from(atob(base64String), (c) => c.charCodeAt(0));\n        this._dataPromise = Promise.resolve(binaryArray.buffer);\n        return this._dataPromise;\n      }\n\n      this._dataPromise = fetch(resolveUri(this.json.uri, this.baseUrl))\n          .then((response) => response.arrayBuffer());\n    }\n    return this._dataPromise;\n  }\n\n  texture(bufferViews) {\n    if (!this._texture) {\n      let img = new Image();\n      this._texture = new ImageTexture(img);\n\n      if (this.json.uri) {\n        if (isDataUri(this.json.uri)) {\n          img.src = this.json.uri;\n        } else {\n          img.src = `${this.baseUrl}${this.json.uri}`;\n        }\n      } else {\n        let view = bufferViews[this.json.bufferView];\n        view.dataView().then((dataView) => {\n          let blob = new Blob([dataView], {type: this.json.mimeType});\n          img.src = window.URL.createObjectURL(blob);\n        });\n      }\n    }\n    return this._texture;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Material} from '../core/material.js';\nimport {ATTRIB_MASK} from '../core/renderer.js';\n\nconst VERTEX_SOURCE = `\nattribute vec3 POSITION, NORMAL;\nattribute vec2 TEXCOORD_0, TEXCOORD_1;\n\nuniform vec3 CAMERA_POSITION;\nuniform vec3 LIGHT_DIRECTION;\n\nvarying vec3 vLight; // Vector from vertex to light.\nvarying vec3 vView; // Vector from vertex to camera.\nvarying vec2 vTex;\n\n#ifdef USE_NORMAL_MAP\nattribute vec4 TANGENT;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_VERTEX_COLOR\nattribute vec4 COLOR_0;\nvarying vec4 vCol;\n#endif\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vec3 n = normalize(vec3(model * vec4(NORMAL, 0.0)));\n#ifdef USE_NORMAL_MAP\n  vec3 t = normalize(vec3(model * vec4(TANGENT.xyz, 0.0)));\n  vec3 b = cross(n, t) * TANGENT.w;\n  vTBN = mat3(t, b, n);\n#else\n  vNorm = n;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  vCol = COLOR_0;\n#endif\n\n  vTex = TEXCOORD_0;\n  vec4 mPos = model * vec4(POSITION, 1.0);\n  vLight = -LIGHT_DIRECTION;\n  vView = CAMERA_POSITION - mPos.xyz;\n  return proj * view * mPos;\n}`;\n\n// These equations are borrowed with love from this docs from Epic because I\n// just don't have anything novel to bring to the PBR scene.\n// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf\nconst EPIC_PBR_FUNCTIONS = `\nvec3 lambertDiffuse(vec3 cDiff) {\n  return cDiff / M_PI;\n}\n\nfloat specD(float a, float nDotH) {\n  float aSqr = a * a;\n  float f = ((nDotH * nDotH) * (aSqr - 1.0) + 1.0);\n  return aSqr / (M_PI * f * f);\n}\n\nfloat specG(float roughness, float nDotL, float nDotV) {\n  float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;\n  float gl = nDotL / (nDotL * (1.0 - k) + k);\n  float gv = nDotV / (nDotV * (1.0 - k) + k);\n  return gl * gv;\n}\n\nvec3 specF(float vDotH, vec3 F0) {\n  float exponent = (-5.55473 * vDotH - 6.98316) * vDotH;\n  float base = 2.0;\n  return F0 + (1.0 - F0) * pow(base, exponent);\n}`;\n\nconst FRAGMENT_SOURCE = `\n#define M_PI 3.14159265\n\nuniform vec4 baseColorFactor;\n#ifdef USE_BASE_COLOR_MAP\nuniform sampler2D baseColorTex;\n#endif\n\nvarying vec3 vLight;\nvarying vec3 vView;\nvarying vec2 vTex;\n\n#ifdef USE_VERTEX_COLOR\nvarying vec4 vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\nuniform sampler2D normalTex;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_METAL_ROUGH_MAP\nuniform sampler2D metallicRoughnessTex;\n#endif\nuniform vec2 metallicRoughnessFactor;\n\n#ifdef USE_OCCLUSION\nuniform sampler2D occlusionTex;\nuniform float occlusionStrength;\n#endif\n\n#ifdef USE_EMISSIVE_TEXTURE\nuniform sampler2D emissiveTex;\n#endif\nuniform vec3 emissiveFactor;\n\nuniform vec3 LIGHT_COLOR;\n\nconst vec3 dielectricSpec = vec3(0.04);\nconst vec3 black = vec3(0.0);\n\n${EPIC_PBR_FUNCTIONS}\n\nvec4 fragment_main() {\n#ifdef USE_BASE_COLOR_MAP\n  vec4 baseColor = texture2D(baseColorTex, vTex) * baseColorFactor;\n#else\n  vec4 baseColor = baseColorFactor;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  baseColor *= vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\n  vec3 n = texture2D(normalTex, vTex).rgb;\n  n = normalize(vTBN * (2.0 * n - 1.0));\n#else\n  vec3 n = normalize(vNorm);\n#endif\n\n#ifdef FULLY_ROUGH\n  float metallic = 0.0;\n#else\n  float metallic = metallicRoughnessFactor.x;\n#endif\n\n  float roughness = metallicRoughnessFactor.y;\n\n#ifdef USE_METAL_ROUGH_MAP\n  vec4 metallicRoughness = texture2D(metallicRoughnessTex, vTex);\n  metallic *= metallicRoughness.b;\n  roughness *= metallicRoughness.g;\n#endif\n  \n  vec3 l = normalize(vLight);\n  vec3 v = normalize(vView);\n  vec3 h = normalize(l+v);\n\n  float nDotL = clamp(dot(n, l), 0.001, 1.0);\n  float nDotV = abs(dot(n, v)) + 0.001;\n  float nDotH = max(dot(n, h), 0.0);\n  float vDotH = max(dot(v, h), 0.0);\n\n  // From GLTF Spec\n  vec3 cDiff = mix(baseColor.rgb * (1.0 - dielectricSpec.r), black, metallic); // Diffuse color\n  vec3 F0 = mix(dielectricSpec, baseColor.rgb, metallic); // Specular color\n  float a = roughness * roughness;\n\n#ifdef FULLY_ROUGH\n  vec3 specular = F0 * 0.45;\n#else\n  vec3 F = specF(vDotH, F0);\n  float D = specD(a, nDotH);\n  float G = specG(roughness, nDotL, nDotV);\n  vec3 specular = (D * F * G) / (4.0 * nDotL * nDotV);\n#endif\n  float halfLambert = dot(n, l) * 0.5 + 0.5;\n  halfLambert *= halfLambert;\n\n  vec3 color = (halfLambert * LIGHT_COLOR * lambertDiffuse(cDiff)) + specular;\n\n#ifdef USE_OCCLUSION\n  float occlusion = texture2D(occlusionTex, vTex).r;\n  color = mix(color, color * occlusion, occlusionStrength);\n#endif\n  \n  vec3 emissive = emissiveFactor;\n#ifdef USE_EMISSIVE_TEXTURE\n  emissive *= texture2D(emissiveTex, vTex).rgb;\n#endif\n  color += emissive;\n\n  // gamma correction\n  //color = pow(color, vec3(1.0/2.2));\n\n  return vec4(color, baseColor.a);\n}`;\n\nexport class PbrMaterial extends Material {\n  constructor() {\n    super();\n\n    this.baseColor = this.defineSampler('baseColorTex');\n    this.metallicRoughness = this.defineSampler('metallicRoughnessTex');\n    this.normal = this.defineSampler('normalTex');\n    this.occlusion = this.defineSampler('occlusionTex');\n    this.emissive = this.defineSampler('emissiveTex');\n\n    this.baseColorFactor = this.defineUniform('baseColorFactor', [1.0, 1.0, 1.0, 1.0]);\n    this.metallicRoughnessFactor = this.defineUniform('metallicRoughnessFactor', [1.0, 1.0]);\n    this.occlusionStrength = this.defineUniform('occlusionStrength', 1.0);\n    this.emissiveFactor = this.defineUniform('emissiveFactor', [0, 0, 0]);\n  }\n\n  get materialName() {\n    return 'PBR';\n  }\n\n  get vertexSource() {\n    return VERTEX_SOURCE;\n  }\n\n  get fragmentSource() {\n    return FRAGMENT_SOURCE;\n  }\n\n  getProgramDefines(renderPrimitive) {\n    let programDefines = {};\n\n    if (renderPrimitive._attributeMask & ATTRIB_MASK.COLOR_0) {\n      programDefines['USE_VERTEX_COLOR'] = 1;\n    }\n\n    if (renderPrimitive._attributeMask & ATTRIB_MASK.TEXCOORD_0) {\n      if (this.baseColor.texture) {\n        programDefines['USE_BASE_COLOR_MAP'] = 1;\n      }\n\n      if (this.normal.texture && (renderPrimitive._attributeMask & ATTRIB_MASK.TANGENT)) {\n        programDefines['USE_NORMAL_MAP'] = 1;\n      }\n\n      if (this.metallicRoughness.texture) {\n        programDefines['USE_METAL_ROUGH_MAP'] = 1;\n      }\n\n      if (this.occlusion.texture) {\n        programDefines['USE_OCCLUSION'] = 1;\n      }\n\n      if (this.emissive.texture) {\n        programDefines['USE_EMISSIVE_TEXTURE'] = 1;\n      }\n    }\n\n    if ((!this.metallicRoughness.texture ||\n         !(renderPrimitive._attributeMask & ATTRIB_MASK.TEXCOORD_0)) &&\n        this.metallicRoughnessFactor.value[1] == 1.0) {\n      programDefines['FULLY_ROUGH'] = 1;\n    }\n\n    return programDefines;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport * as glMatrix from '../../node_modules/gl-matrix/src/gl-matrix/common.js';\nimport * as mat2 from '../../node_modules/gl-matrix/src/gl-matrix/mat2.js';\nimport * as mat2d from '../../node_modules/gl-matrix/src/gl-matrix/mat2d.js';\nimport * as mat3 from '../../node_modules/gl-matrix/src/gl-matrix/mat3.js';\nimport * as mat4 from '../../node_modules/gl-matrix/src/gl-matrix/mat4.js';\nimport * as quat from '../../node_modules/gl-matrix/src/gl-matrix/quat.js';\nimport * as quat2 from '../../node_modules/gl-matrix/src/gl-matrix/quat2.js';\nimport * as vec2 from '../../node_modules/gl-matrix/src/gl-matrix/vec2.js';\nimport * as vec3 from '../../node_modules/gl-matrix/src/gl-matrix/vec3.js';\nimport * as vec4 from '../../node_modules/gl-matrix/src/gl-matrix/vec4.js';\n\nexport {\n  glMatrix,\n  mat2,\n  mat2d,\n  mat3,\n  mat4,\n  quat,\n  quat2,\n  vec2,\n  vec3,\n  vec4,\n};\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {mat3, vec3} from './gl-matrix.js';\n\nlet normalMat = mat3.create();\n\nconst RAY_INTERSECTION_OFFSET = 0.02;\n\nexport class Ray {\n  constructor(matrix = null) {\n    this.origin = vec3.create();\n\n    this._dir = vec3.create();\n    this._dir[2] = -1.0;\n\n    if (matrix) {\n      vec3.transformMat4(this.origin, this.origin, matrix);\n      mat3.fromMat4(normalMat, matrix);\n      vec3.transformMat3(this._dir, this._dir, normalMat);\n    }\n\n    // To force the inverse and sign calculations.\n    this.dir = this._dir;\n  }\n\n  get dir() {\n    return this._dir;\n  }\n\n  set dir(value) {\n    this._dir = vec3.copy(this._dir, value);\n    vec3.normalize(this._dir, this._dir);\n\n    this.inv_dir = vec3.fromValues(\n      1.0 / this._dir[0],\n      1.0 / this._dir[1],\n      1.0 / this._dir[2]);\n\n    this.sign = [\n      (this.inv_dir[0] < 0) ? 1 : 0,\n      (this.inv_dir[1] < 0) ? 1 : 0,\n      (this.inv_dir[2] < 0) ? 1 : 0,\n    ];\n  }\n\n  // Borrowed from:\n  // eslint-disable-next-line max-len\n  // https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection\n  intersectsAABB(min, max) {\n    let r = this;\n\n    let bounds = [min, max];\n\n    let tmin = (bounds[r.sign[0]][0] - r.origin[0]) * r.inv_dir[0];\n    let tmax = (bounds[1-r.sign[0]][0] - r.origin[0]) * r.inv_dir[0];\n    let tymin = (bounds[r.sign[1]][1] - r.origin[1]) * r.inv_dir[1];\n    let tymax = (bounds[1-r.sign[1]][1] - r.origin[1]) * r.inv_dir[1];\n\n    if ((tmin > tymax) || (tymin > tmax)) {\n      return null;\n    }\n    if (tymin > tmin) {\n      tmin = tymin;\n    }\n    if (tymax < tmax) {\n      tmax = tymax;\n    }\n\n    let tzmin = (bounds[r.sign[2]][2] - r.origin[2]) * r.inv_dir[2];\n    let tzmax = (bounds[1-r.sign[2]][2] - r.origin[2]) * r.inv_dir[2];\n\n    if ((tmin > tzmax) || (tzmin > tmax)) {\n      return null;\n    }\n    if (tzmin > tmin) {\n      tmin = tzmin;\n    }\n    if (tzmax < tmax) {\n      tmax = tzmax;\n    }\n\n    let t = -1;\n    if (tmin > 0 && tmax > 0) {\n      t = Math.min(tmin, tmax);\n    } else if (tmin > 0) {\n      t = tmin;\n    } else if (tmax > 0) {\n      t = tmax;\n    } else {\n      // Intersection is behind the ray origin.\n      return null;\n    }\n\n    // Push ray intersection point back along the ray a bit so that cursors\n    // don't accidentally intersect with the hit surface.\n    t -= RAY_INTERSECTION_OFFSET;\n\n    // Return the point where the ray first intersected with the AABB.\n    let intersectionPoint = vec3.clone(this._dir);\n    vec3.scale(intersectionPoint, intersectionPoint, t);\n    vec3.add(intersectionPoint, intersectionPoint, this.origin);\n    return intersectionPoint;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nThis file renders a passed in XRStageBounds object and attempts\nto render geometry on the floor to indicate where the bounds is.\nXRStageBounds' `geometry` is a series of XRStageBoundsPoints (in\nclockwise-order) with `x` and `z` properties for each.\n*/\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nclass BoundsMaterial extends Material {\n  constructor() {\n    super();\n\n    this.state.blend = true;\n    this.state.blendFuncSrc = GL.SRC_ALPHA;\n    this.state.blendFuncDst = GL.ONE;\n    this.state.depthTest = false;\n  }\n\n  get materialName() {\n    return 'BOUNDS_RENDERER';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    precision mediump float;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 1.0, 0.0, 0.3);\n    }`;\n  }\n}\n\nexport class BoundsRenderer extends Node {\n  constructor() {\n    super();\n\n    this._stageBounds = null;\n  }\n\n  onRendererChanged(renderer) {\n    this.stageBounds = this._stageBounds;\n  }\n\n  get stageBounds() {\n    return this._stageBounds;\n  }\n\n  set stageBounds(stageBounds) {\n    if (this._stageBounds) {\n      this.clearRenderPrimitives();\n    }\n    this._stageBounds = stageBounds;\n    if (!stageBounds || stageBounds.length === 0 || !this._renderer) {\n      return;\n    }\n\n    let verts = [];\n    let indices = [];\n\n    // Tessellate the bounding points from XRStageBounds and connect\n    // each point to a neighbor and 0,0,0.\n    const pointCount = stageBounds.geometry.length;\n    for (let i = 0; i < pointCount; i++) {\n      const point = stageBounds.geometry[i];\n      verts.push(point.x, 0, point.z);\n      indices.push(i, i === 0 ? pointCount - 1 : i - 1, pointCount);\n    }\n    // Center point\n    verts.push(0, 0, 0);\n\n    let vertexBuffer = this._renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(verts));\n    let indexBuffer = this._renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));\n\n    let attribs = [\n      new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 12, 0),\n    ];\n\n    let primitive = new Primitive(attribs, indices.length);\n    primitive.setIndexBuffer(indexBuffer);\n\n    let renderPrimitive = this._renderer.createRenderPrimitive(primitive, new BoundsMaterial());\n    this.addRenderPrimitive(renderPrimitive);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {PrimitiveStream} from '../geometry/primitive-stream.js';\n\nconst BUTTON_SIZE = 0.1;\nconst BUTTON_CORNER_RADIUS = 0.025;\nconst BUTTON_CORNER_SEGMENTS = 8;\nconst BUTTON_ICON_SIZE = 0.07;\nconst BUTTON_LAYER_DISTANCE = 0.005;\nconst BUTTON_COLOR = 0.75;\nconst BUTTON_ALPHA = 0.85;\nconst BUTTON_HOVER_COLOR = 0.9;\nconst BUTTON_HOVER_ALPHA = 1.0;\nconst BUTTON_HOVER_SCALE = 1.1;\nconst BUTTON_HOVER_TRANSITION_TIME_MS = 200;\n\nclass ButtonMaterial extends Material {\n  constructor() {\n    super();\n\n    this.state.blend = true;\n\n    this.defineUniform('hoverAmount', 0);\n  }\n\n  get materialName() {\n    return 'BUTTON_MATERIAL';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n\n    uniform float hoverAmount;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      float scale = mix(1.0, ${BUTTON_HOVER_SCALE}, hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    uniform float hoverAmount;\n\n    const vec4 default_color = vec4(${BUTTON_COLOR}, ${BUTTON_COLOR}, ${BUTTON_COLOR}, ${BUTTON_ALPHA});\n    const vec4 hover_color = vec4(${BUTTON_HOVER_COLOR}, ${BUTTON_HOVER_COLOR},\n                                  ${BUTTON_HOVER_COLOR}, ${BUTTON_HOVER_ALPHA});\n\n    vec4 fragment_main() {\n      return mix(default_color, hover_color, hoverAmount);\n    }`;\n  }\n}\n\nclass ButtonIconMaterial extends Material {\n  constructor() {\n    super();\n\n    this.state.blend = true;\n\n    this.defineUniform('hoverAmount', 0);\n    this.icon = this.defineSampler('icon');\n  }\n\n  get materialName() {\n    return 'BUTTON_ICON_MATERIAL';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    uniform float hoverAmount;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      float scale = mix(1.0, ${BUTTON_HOVER_SCALE}, hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    uniform sampler2D icon;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(icon, vTexCoord);\n    }`;\n  }\n}\n\nexport class ButtonNode extends Node {\n  constructor(iconTexture, callback) {\n    super();\n\n    // All buttons are selectable by default.\n    this.selectable = true;\n\n    this._selectHandler = callback;\n    this._iconTexture = iconTexture;\n    this._hovered = false;\n    this._hoverT = 0;\n  }\n\n  get iconTexture() {\n    return this._iconTexture;\n  }\n\n  set iconTexture(value) {\n    if (this._iconTexture == value) {\n      return;\n    }\n\n    this._iconTexture = value;\n    this._iconRenderPrimitive.samplers.icon.texture = value;\n  }\n\n  onRendererChanged(renderer) {\n    let stream = new PrimitiveStream();\n\n    let hd = BUTTON_LAYER_DISTANCE * 0.5;\n\n    // Build a rounded rect for the background.\n    let hs = BUTTON_SIZE * 0.5;\n    let ihs = hs - BUTTON_CORNER_RADIUS;\n    stream.startGeometry();\n\n    // Rounded corners and sides\n    let segments = BUTTON_CORNER_SEGMENTS * 4;\n    for (let i = 0; i < segments; ++i) {\n      let rad = i * ((Math.PI * 2.0) / segments);\n      let x = Math.cos(rad) * BUTTON_CORNER_RADIUS;\n      let y = Math.sin(rad) * BUTTON_CORNER_RADIUS;\n      let section = Math.floor(i / BUTTON_CORNER_SEGMENTS);\n      switch (section) {\n        case 0:\n          x += ihs;\n          y += ihs;\n          break;\n        case 1:\n          x -= ihs;\n          y += ihs;\n          break;\n        case 2:\n          x -= ihs;\n          y -= ihs;\n          break;\n        case 3:\n          x += ihs;\n          y -= ihs;\n          break;\n      }\n\n      stream.pushVertex(x, y, -hd, 0, 0, 0, 0, 1);\n\n      if (i > 1) {\n        stream.pushTriangle(0, i-1, i);\n      }\n    }\n\n    stream.endGeometry();\n\n    let buttonPrimitive = stream.finishPrimitive(renderer);\n    this._buttonRenderPrimitive = renderer.createRenderPrimitive(buttonPrimitive, new ButtonMaterial());\n    this.addRenderPrimitive(this._buttonRenderPrimitive);\n\n    // Build a simple textured quad for the foreground.\n    hs = BUTTON_ICON_SIZE * 0.5;\n    stream.clear();\n    stream.startGeometry();\n\n    stream.pushVertex(-hs, hs, hd, 0, 0, 0, 0, 1);\n    stream.pushVertex(-hs, -hs, hd, 0, 1, 0, 0, 1);\n    stream.pushVertex(hs, -hs, hd, 1, 1, 0, 0, 1);\n    stream.pushVertex(hs, hs, hd, 1, 0, 0, 0, 1);\n\n    stream.pushTriangle(0, 1, 2);\n    stream.pushTriangle(0, 2, 3);\n\n    stream.endGeometry();\n\n    let iconPrimitive = stream.finishPrimitive(renderer);\n    let iconMaterial = new ButtonIconMaterial();\n    iconMaterial.icon.texture = this._iconTexture;\n    this._iconRenderPrimitive = renderer.createRenderPrimitive(iconPrimitive, iconMaterial);\n    this.addRenderPrimitive(this._iconRenderPrimitive);\n  }\n\n  onHoverStart() {\n    this._hovered = true;\n  }\n\n  onHoverEnd() {\n    this._hovered = false;\n  }\n\n  _updateHoverState() {\n    let t = this._hoverT / BUTTON_HOVER_TRANSITION_TIME_MS;\n    // Cubic Ease In/Out\n    // TODO: Get a better animation system\n    let hoverAmount = t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1;\n    this._buttonRenderPrimitive.uniforms.hoverAmount.value = hoverAmount;\n    this._iconRenderPrimitive.uniforms.hoverAmount.value = hoverAmount;\n  }\n\n  onUpdate(timestamp, frameDelta) {\n    if (this._hovered && this._hoverT < BUTTON_HOVER_TRANSITION_TIME_MS) {\n      this._hoverT = Math.min(BUTTON_HOVER_TRANSITION_TIME_MS, this._hoverT + frameDelta);\n      this._updateHoverState();\n    } else if (!this._hovered && this._hoverT > 0) {\n      this._hoverT = Math.max(0.0, this._hoverT - frameDelta);\n      this._updateHoverState();\n    }\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {UrlTexture} from '../core/texture.js';\nimport {BoxBuilder} from '../geometry/box-builder.js';\nimport {mat4} from '../math/gl-matrix.js';\n\nclass CubeSeaMaterial extends Material {\n  constructor(heavy = false) {\n    super();\n\n    this.heavy = heavy;\n\n    this.baseColor = this.defineSampler('baseColor');\n  }\n\n  get materialName() {\n    return 'CUBE_SEA';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    attribute vec3 NORMAL;\n\n    varying vec2 vTexCoord;\n    varying vec3 vLight;\n\n    const vec3 lightDir = vec3(0.75, 0.5, 1.0);\n    const vec3 ambientColor = vec3(0.5, 0.5, 0.5);\n    const vec3 lightColor = vec3(0.75, 0.75, 0.75);\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec3 normalRotated = vec3(model * vec4(NORMAL, 0.0));\n      float lightFactor = max(dot(normalize(lightDir), normalRotated), 0.0);\n      vLight = ambientColor + (lightColor * lightFactor);\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    if (!this.heavy) {\n      return `\n      precision mediump float;\n      uniform sampler2D baseColor;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec4 fragment_main() {\n        return vec4(vLight, 1.0) * texture2D(baseColor, vTexCoord);\n      }`;\n    } else {\n      // Used when we want to stress the GPU a bit more.\n      // Stolen with love from https://www.clicktorelease.com/code/codevember-2016/4/\n      return `\n      precision mediump float;\n\n      uniform sampler2D diffuse;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec2 dimensions = vec2(64, 64);\n      float seed = 0.42;\n\n      vec2 hash( vec2 p ) {\n        p=vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3)));\n        return fract(sin(p)*18.5453);\n      }\n\n      vec3 hash3( vec2 p ) {\n          vec3 q = vec3( dot(p,vec2(127.1,311.7)),\n                 dot(p,vec2(269.5,183.3)),\n                 dot(p,vec2(419.2,371.9)) );\n        return fract(sin(q)*43758.5453);\n      }\n\n      float iqnoise( in vec2 x, float u, float v ) {\n        vec2 p = floor(x);\n        vec2 f = fract(x);\n        float k = 1.0+63.0*pow(1.0-v,4.0);\n        float va = 0.0;\n        float wt = 0.0;\n        for( int j=-2; j<=2; j++ )\n          for( int i=-2; i<=2; i++ ) {\n            vec2 g = vec2( float(i),float(j) );\n            vec3 o = hash3( p + g )*vec3(u,u,1.0);\n            vec2 r = g - f + o.xy;\n            float d = dot(r,r);\n            float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n            va += o.z*ww;\n            wt += ww;\n          }\n        return va/wt;\n      }\n\n      // return distance, and cell id\n      vec2 voronoi( in vec2 x ) {\n        vec2 n = floor( x );\n        vec2 f = fract( x );\n        vec3 m = vec3( 8.0 );\n        for( int j=-1; j<=1; j++ )\n          for( int i=-1; i<=1; i++ ) {\n            vec2  g = vec2( float(i), float(j) );\n            vec2  o = hash( n + g );\n            vec2  r = g - f + (0.5+0.5*sin(seed+6.2831*o));\n            float d = dot( r, r );\n            if( d<m.x )\n              m = vec3( d, o );\n          }\n        return vec2( sqrt(m.x), m.y+m.z );\n      }\n\n      vec4 fragment_main() {\n        vec2 uv = ( vTexCoord );\n        uv *= vec2( 10., 10. );\n        uv += seed;\n        vec2 p = 0.5 - 0.5*sin( 0.*vec2(1.01,1.71) );\n\n        vec2 c = voronoi( uv );\n        vec3 col = vec3( c.y / 2. );\n\n        float f = iqnoise( 1. * uv + c.y, p.x, p.y );\n        col *= 1.0 + .25 * vec3( f );\n\n        return vec4(vLight, 1.0) * texture2D(diffuse, vTexCoord) * vec4( col, 1. );\n      }`;\n    }\n  }\n}\n\nexport class CubeSeaNode extends Node {\n  constructor(options = {}) {\n    super();\n\n    // Test variables\n    // If true, use a very heavyweight shader to stress the GPU.\n    this.heavyGpu = !!options.heavyGpu;\n\n    // Number and size of the static cubes. Warning, large values\n    // don't render right due to overflow of the int16 indices.\n    this.cubeCount = options.cubeCount || (this.heavyGpu ? 12 : 10);\n    this.cubeScale = options.cubeScale || 1.0;\n\n    // Draw only half the world cubes. Helps test variable render cost\n    // when combined with heavyGpu.\n    this.halfOnly = !!options.halfOnly;\n\n    // Automatically spin the world cubes. Intended for automated testing,\n    // not recommended for viewing in a headset.\n    this.autoRotate = !!options.autoRotate;\n\n    this._texture = new UrlTexture(options.imageUrl || 'media/textures/cube-sea.png');\n\n    this._material = new CubeSeaMaterial(this.heavyGpu);\n    this._material.baseColor.texture = this._texture;\n\n    this._renderPrimitive = null;\n  }\n\n  onRendererChanged(renderer) {\n    this._renderPrimitive = null;\n\n    let boxBuilder = new BoxBuilder();\n\n    // Build the spinning \"hero\" cubes\n    boxBuilder.pushCube([0, 0.25, -0.8], 0.1);\n    boxBuilder.pushCube([0.8, 0.25, 0], 0.1);\n    boxBuilder.pushCube([0, 0.25, 0.8], 0.1);\n    boxBuilder.pushCube([-0.8, 0.25, 0], 0.1);\n\n    let heroPrimitive = boxBuilder.finishPrimitive(renderer);\n\n    this.heroNode = renderer.createMesh(heroPrimitive, this._material);\n\n    this.rebuildCubes(boxBuilder);\n\n    this.cubeSeaNode = new Node();\n    this.cubeSeaNode.addRenderPrimitive(this._renderPrimitive);\n\n    this.addNode(this.cubeSeaNode);\n    this.addNode(this.heroNode);\n\n    return this.waitForComplete();\n  }\n\n  rebuildCubes(boxBuilder) {\n    if (!this._renderer) {\n      return;\n    }\n\n    if (!boxBuilder) {\n      boxBuilder = new BoxBuilder();\n    } else {\n      boxBuilder.clear();\n    }\n\n    let size = 0.4 * this.cubeScale;\n\n    // Build the cube sea\n    let halfGrid = this.cubeCount * 0.5;\n    for (let x = 0; x < this.cubeCount; ++x) {\n      for (let y = 0; y < this.cubeCount; ++y) {\n        for (let z = 0; z < this.cubeCount; ++z) {\n          let pos = [x - halfGrid, y - halfGrid, z - halfGrid];\n          // Only draw cubes on one side. Useful for testing variable render\n          // cost that depends on view direction.\n          if (this.halfOnly && pos[0] < 0) {\n            continue;\n          }\n\n          // Don't place a cube in the center of the grid.\n          if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0) {\n            continue;\n          }\n\n          boxBuilder.pushCube(pos, size);\n        }\n      }\n    }\n\n    if (this.cubeCount > 12) {\n      // Each cube has 6 sides with 2 triangles and 3 indices per triangle, so\n      // the total number of indices needed is cubeCount^3 * 36. This exceeds\n      // the short index range past 12 cubes.\n      boxBuilder.indexType = 5125; // gl.UNSIGNED_INT\n    }\n    let cubeSeaPrimitive = boxBuilder.finishPrimitive(this._renderer);\n\n    if (!this._renderPrimitive) {\n      this._renderPrimitive = this._renderer.createRenderPrimitive(cubeSeaPrimitive, this._material);\n    } else {\n      this._renderPrimitive.setPrimitive(cubeSeaPrimitive);\n    }\n  }\n\n  onUpdate(timestamp, frameDelta) {\n    if (this.autoRotate) {\n      mat4.fromRotation(this.cubeSeaNode.matrix, timestamp / 500, [0, -1, 0]);\n    }\n    mat4.fromRotation(this.heroNode.matrix, timestamp / 2000, [0, 1, 0]);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {PrimitiveStream} from '../geometry/primitive-stream.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nconst SHADOW_SEGMENTS = 32;\nconst SHADOW_GROUND_OFFSET = 0.01;\nconst SHADOW_CENTER_ALPHA = 0.7;\nconst SHADOW_INNER_ALPHA = 0.3;\nconst SHADOW_OUTER_ALPHA = 0.0;\nconst SHADOW_INNER_RADIUS = 0.6;\nconst SHADOW_OUTER_RADIUS = 1.0;\n\nclass DropShadowMaterial extends Material {\n  constructor() {\n    super();\n\n    this.state.blend = true;\n    this.state.blendFuncSrc = GL.ONE;\n    this.state.blendFuncDst = GL.ONE_MINUS_SRC_ALPHA;\n    this.state.depthFunc = GL.LEQUAL;\n    this.state.depthMask = false;\n  }\n\n  get materialName() {\n    return 'DROP_SHADOW_MATERIAL';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying float vShadow;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vShadow = TEXCOORD_0.x;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    varying float vShadow;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 0.0, 0.0, vShadow);\n    }`;\n  }\n}\n\nexport class DropShadowNode extends Node {\n  constructor(iconTexture, callback) {\n    super();\n  }\n\n  onRendererChanged(renderer) {\n    let stream = new PrimitiveStream();\n\n    stream.startGeometry();\n\n    // Shadow center\n    stream.pushVertex(0, SHADOW_GROUND_OFFSET, 0, SHADOW_CENTER_ALPHA);\n\n    let segRad = ((Math.PI * 2.0) / SHADOW_SEGMENTS);\n\n    let idx;\n    for (let i = 0; i < SHADOW_SEGMENTS; ++i) {\n      idx = stream.nextVertexIndex;\n\n      let rad = i * segRad;\n      let x = Math.cos(rad);\n      let y = Math.sin(rad);\n      stream.pushVertex(x * SHADOW_INNER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_INNER_RADIUS, SHADOW_INNER_ALPHA);\n      stream.pushVertex(x * SHADOW_OUTER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_OUTER_RADIUS, SHADOW_OUTER_ALPHA);\n\n      if (i > 0) {\n        // Inner circle\n        stream.pushTriangle(0, idx, idx-2);\n\n        // Outer circle\n        stream.pushTriangle(idx, idx+1, idx-1);\n        stream.pushTriangle(idx, idx-1, idx-2);\n      }\n    }\n\n    stream.pushTriangle(0, 1, idx);\n\n    stream.pushTriangle(1, 2, idx+1);\n    stream.pushTriangle(1, idx+1, idx);\n\n    stream.endGeometry();\n\n    let shadowPrimitive = stream.finishPrimitive(renderer);\n    this._shadowRenderPrimitive = renderer.createRenderPrimitive(shadowPrimitive, new DropShadowMaterial());\n    this.addRenderPrimitive(this._shadowRenderPrimitive);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Node} from '../core/node.js';\nimport {Gltf2Loader} from '../loaders/gltf2.js';\n\n// Using a weak map here allows us to cache a loader per-renderer without\n// modifying the renderer object or leaking memory when it's garbage collected.\nlet gltfLoaderMap = new WeakMap();\n\nexport class Gltf2Node extends Node {\n  constructor(options) {\n    super();\n    this._url = options.url;\n\n    this._promise = null;\n    this._resolver = null;\n    this._rejecter = null;\n  }\n\n  onRendererChanged(renderer) {\n    let loader = gltfLoaderMap.get(renderer);\n    if (!loader) {\n      loader = new Gltf2Loader(renderer);\n      gltfLoaderMap.set(renderer, loader);\n    }\n\n    // Do we have a previously resolved promise? If so clear it.\n    if (!this._resolver && this._promise) {\n      this._promise = null;\n    }\n\n    this._ensurePromise();\n\n    loader.loadFromUrl(this._url).then((sceneNode) => {\n      this.addNode(sceneNode);\n      this._resolver(sceneNode.waitForComplete());\n      this._resolver = null;\n      this._rejecter = null;\n    }).catch((err) => {\n      this._rejecter(err);\n      this._resolver = null;\n      this._rejecter = null;\n    });\n  }\n\n  _ensurePromise() {\n    if (!this._promise) {\n      this._promise = new Promise((resolve, reject) => {\n        this._resolver = resolve;\n        this._rejecter = reject;\n      });\n    }\n    return this._promise;\n  }\n\n  waitForComplete() {\n    return this._ensurePromise();\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {Material, RENDER_ORDER} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {DataTexture} from '../core/texture.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\n// Laser texture data, 48x1 RGBA (not premultiplied alpha). This represents a\n// \"cross section\" of the laser beam with a bright core and a feathered edge.\n// Borrowed from Chromium source code.\nconst LASER_TEXTURE_DATA = new Uint8Array([\n0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0xbf, 0xbf, 0xbf, 0x04, 0xcc, 0xcc, 0xcc, 0x05,\n0xdb, 0xdb, 0xdb, 0x07, 0xcc, 0xcc, 0xcc, 0x0a, 0xd8, 0xd8, 0xd8, 0x0d, 0xd2, 0xd2, 0xd2, 0x11,\n0xce, 0xce, 0xce, 0x15, 0xce, 0xce, 0xce, 0x1a, 0xce, 0xce, 0xce, 0x1f, 0xcd, 0xcd, 0xcd, 0x24,\n0xc8, 0xc8, 0xc8, 0x2a, 0xc9, 0xc9, 0xc9, 0x2f, 0xc9, 0xc9, 0xc9, 0x34, 0xc9, 0xc9, 0xc9, 0x39,\n0xc9, 0xc9, 0xc9, 0x3d, 0xc8, 0xc8, 0xc8, 0x41, 0xcb, 0xcb, 0xcb, 0x44, 0xee, 0xee, 0xee, 0x87,\n0xfa, 0xfa, 0xfa, 0xc8, 0xf9, 0xf9, 0xf9, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xfa, 0xfa, 0xfa, 0xc9,\n0xfa, 0xfa, 0xfa, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xf9, 0xf9, 0xf9, 0xc9, 0xfa, 0xfa, 0xfa, 0xc8,\n0xee, 0xee, 0xee, 0x87, 0xcb, 0xcb, 0xcb, 0x44, 0xc8, 0xc8, 0xc8, 0x41, 0xc9, 0xc9, 0xc9, 0x3d,\n0xc9, 0xc9, 0xc9, 0x39, 0xc9, 0xc9, 0xc9, 0x34, 0xc9, 0xc9, 0xc9, 0x2f, 0xc8, 0xc8, 0xc8, 0x2a,\n0xcd, 0xcd, 0xcd, 0x24, 0xce, 0xce, 0xce, 0x1f, 0xce, 0xce, 0xce, 0x1a, 0xce, 0xce, 0xce, 0x15,\n0xd2, 0xd2, 0xd2, 0x11, 0xd8, 0xd8, 0xd8, 0x0d, 0xcc, 0xcc, 0xcc, 0x0a, 0xdb, 0xdb, 0xdb, 0x07,\n0xcc, 0xcc, 0xcc, 0x05, 0xbf, 0xbf, 0xbf, 0x04, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x01,\n]);\n\nconst LASER_LENGTH = 1.0;\nconst LASER_DIAMETER = 0.01;\nconst LASER_FADE_END = 0.535;\nconst LASER_FADE_POINT = 0.5335;\nconst LASER_DEFAULT_COLOR = [1.0, 1.0, 1.0, 0.25];\n\nconst CURSOR_RADIUS = 0.004;\nconst CURSOR_SHADOW_RADIUS = 0.007;\nconst CURSOR_SHADOW_INNER_LUMINANCE = 0.5;\nconst CURSOR_SHADOW_OUTER_LUMINANCE = 0.0;\nconst CURSOR_SHADOW_INNER_OPACITY = 0.75;\nconst CURSOR_SHADOW_OUTER_OPACITY = 0.0;\nconst CURSOR_OPACITY = 0.9;\nconst CURSOR_SEGMENTS = 16;\nconst CURSOR_DEFAULT_COLOR = [1.0, 1.0, 1.0, 1.0];\nconst CURSOR_DEFAULT_HIDDEN_COLOR = [0.5, 0.5, 0.5, 0.25];\n\nconst DEFAULT_RESET_OPTIONS = {\n  controllers: true,\n  lasers: true,\n  cursors: true,\n};\n\nclass LaserMaterial extends Material {\n  constructor() {\n    super();\n    this.renderOrder = RENDER_ORDER.ADDITIVE;\n    this.state.cullFace = false;\n    this.state.blend = true;\n    this.state.blendFuncSrc = GL.ONE;\n    this.state.blendFuncDst = GL.ONE;\n    this.state.depthMask = false;\n\n    this.laser = this.defineSampler('diffuse');\n    this.laser.texture = new DataTexture(LASER_TEXTURE_DATA, 48, 1);\n    this.laserColor = this.defineUniform('laserColor', LASER_DEFAULT_COLOR);\n  }\n\n  get materialName() {\n    return 'INPUT_LASER';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    precision mediump float;\n\n    uniform vec4 laserColor;\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    const float fadePoint = ${LASER_FADE_POINT};\n    const float fadeEnd = ${LASER_FADE_END};\n\n    vec4 fragment_main() {\n      vec2 uv = vTexCoord;\n      float front_fade_factor = 1.0 - clamp(1.0 - (uv.y - fadePoint) / (1.0 - fadePoint), 0.0, 1.0);\n      float back_fade_factor = clamp((uv.y - fadePoint) / (fadeEnd - fadePoint), 0.0, 1.0);\n      vec4 color = laserColor * texture2D(diffuse, vTexCoord);\n      float opacity = color.a * front_fade_factor * back_fade_factor;\n      return vec4(color.rgb * opacity, opacity);\n    }`;\n  }\n}\n\nconst CURSOR_VERTEX_SHADER = `\nattribute vec4 POSITION;\n\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vLuminance = POSITION.z;\n  vOpacity = POSITION.w;\n\n  // Billboarded, constant size vertex transform.\n  vec4 screenPos = proj * view * model * vec4(0.0, 0.0, 0.0, 1.0);\n  screenPos /= screenPos.w;\n  screenPos.xy += POSITION.xy;\n  return screenPos;\n}`;\n\nconst CURSOR_FRAGMENT_SHADER = `\nprecision mediump float;\n\nuniform vec4 cursorColor;\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 fragment_main() {\n  vec3 color = cursorColor.rgb * vLuminance;\n  float opacity = cursorColor.a * vOpacity;\n  return vec4(color * opacity, opacity);\n}`;\n\n// Cursors are drawn as billboards that always face the camera and are rendered\n// as a fixed size no matter how far away they are.\nclass CursorMaterial extends Material {\n  constructor() {\n    super();\n    this.renderOrder = RENDER_ORDER.ADDITIVE;\n    this.state.cullFace = false;\n    this.state.blend = true;\n    this.state.blendFuncSrc = GL.ONE;\n    this.state.depthMask = false;\n\n    this.cursorColor = this.defineUniform('cursorColor', CURSOR_DEFAULT_COLOR);\n  }\n\n  get materialName() {\n    return 'INPUT_CURSOR';\n  }\n\n  get vertexSource() {\n    return CURSOR_VERTEX_SHADER;\n  }\n\n  get fragmentSource() {\n    return CURSOR_FRAGMENT_SHADER;\n  }\n}\n\nclass CursorHiddenMaterial extends Material {\n  constructor() {\n    super();\n    this.renderOrder = RENDER_ORDER.ADDITIVE;\n    this.state.cullFace = false;\n    this.state.blend = true;\n    this.state.blendFuncSrc = GL.ONE;\n    this.state.depthFunc = GL.GEQUAL;\n    this.state.depthMask = false;\n\n    this.cursorColor = this.defineUniform('cursorColor', CURSOR_DEFAULT_HIDDEN_COLOR);\n  }\n\n  // TODO: Rename to \"program_name\"\n  get materialName() {\n    return 'INPUT_CURSOR_2';\n  }\n\n  get vertexSource() {\n    return CURSOR_VERTEX_SHADER;\n  }\n\n  get fragmentSource() {\n    return CURSOR_FRAGMENT_SHADER;\n  }\n}\n\nexport class InputRenderer extends Node {\n  constructor() {\n    super();\n\n    this._maxInputElements = 32;\n\n    this._controllers = [];\n    this._controllerNode = null;\n    this._controllerNodeHandedness = null;\n    this._lasers = null;\n    this._cursors = null;\n\n    this._activeControllers = 0;\n    this._activeLasers = 0;\n    this._activeCursors = 0;\n  }\n\n  onRendererChanged(renderer) {\n    this._controllers = [];\n    this._controllerNode = null;\n    this._controllerNodeHandedness = null;\n    this._lasers = null;\n    this._cursors = null;\n\n    this._activeControllers = 0;\n    this._activeLasers = 0;\n    this._activeCursors = 0;\n  }\n\n  setControllerMesh(controllerNode, handedness = 'right') {\n    this._controllerNode = controllerNode;\n    this._controllerNode.visible = false;\n    // FIXME: Temporary fix to initialize for cloning.\n    this.addNode(this._controllerNode);\n    this._controllerNodeHandedness = handedness;\n  }\n\n  addController(gripMatrix) {\n    if (!this._controllerNode) {\n        return;\n    }\n\n    let controller = null;\n    if (this._activeControllers < this._controllers.length) {\n      controller = this._controllers[this._activeControllers];\n    } else {\n      controller = this._controllerNode.clone();\n      this.addNode(controller);\n      this._controllers.push(controller);\n    }\n    this._activeControllers = (this._activeControllers + 1) % this._maxInputElements;\n\n    controller.matrix = gripMatrix;\n    controller.visible = true;\n  }\n\n  addLaserPointer(targetRay) {\n    // Create the laser pointer mesh if needed.\n    if (!this._lasers && this._renderer) {\n      this._lasers = [this._createLaserMesh()];\n      this.addNode(this._lasers[0]);\n    }\n\n    let laser = null;\n    if (this._activeLasers < this._lasers.length) {\n      laser = this._lasers[this._activeLasers];\n    } else {\n      laser = this._lasers[0].clone();\n      this.addNode(laser);\n      this._lasers.push(laser);\n    }\n    this._activeLasers = (this._activeLasers + 1) % this._maxInputElements;\n\n    laser.matrix = targetRay.transformMatrix;\n    laser.visible = true;\n  }\n\n  addCursor(cursorPos) {\n    // Create the cursor mesh if needed.\n    if (!this._cursors && this._renderer) {\n      this._cursors = [this._createCursorMesh()];\n      this.addNode(this._cursors[0]);\n    }\n\n    let cursor = null;\n    if (this._activeCursors < this._cursors.length) {\n      cursor = this._cursors[this._activeCursors];\n    } else {\n      cursor = this._cursors[0].clone();\n      this.addNode(cursor);\n      this._cursors.push(cursor);\n    }\n    this._activeCursors = (this._activeCursors + 1) % this._maxInputElements;\n\n    cursor.translation = cursorPos;\n    cursor.visible = true;\n  }\n\n  reset(options) {\n    if (!options) {\n      options = DEFAULT_RESET_OPTIONS;\n    }\n    if (this._controllers && options.controllers) {\n      for (let controller of this._controllers) {\n        controller.visible = false;\n      }\n      this._activeControllers = 0;\n    }\n    if (this._lasers && options.lasers) {\n      for (let laser of this._lasers) {\n        laser.visible = false;\n      }\n      this._activeLasers = 0;\n    }\n    if (this._cursors && options.cursors) {\n      for (let cursor of this._cursors) {\n        cursor.visible = false;\n      }\n      this._activeCursors = 0;\n    }\n  }\n\n  _createLaserMesh() {\n    let gl = this._renderer._gl;\n\n    let lr = LASER_DIAMETER * 0.5;\n    let ll = LASER_LENGTH;\n\n    // Laser is rendered as cross-shaped beam\n    let laserVerts = [\n    // X    Y   Z    U    V\n      0.0, lr, 0.0, 0.0, 1.0,\n      0.0, lr, -ll, 0.0, 0.0,\n      0.0, -lr, 0.0, 1.0, 1.0,\n      0.0, -lr, -ll, 1.0, 0.0,\n\n      lr, 0.0, 0.0, 0.0, 1.0,\n      lr, 0.0, -ll, 0.0, 0.0,\n      -lr, 0.0, 0.0, 1.0, 1.0,\n      -lr, 0.0, -ll, 1.0, 0.0,\n\n      0.0, -lr, 0.0, 0.0, 1.0,\n      0.0, -lr, -ll, 0.0, 0.0,\n      0.0, lr, 0.0, 1.0, 1.0,\n      0.0, lr, -ll, 1.0, 0.0,\n\n      -lr, 0.0, 0.0, 0.0, 1.0,\n      -lr, 0.0, -ll, 0.0, 0.0,\n      lr, 0.0, 0.0, 1.0, 1.0,\n      lr, 0.0, -ll, 1.0, 0.0,\n    ];\n    let laserIndices = [\n      0, 1, 2, 1, 3, 2,\n      4, 5, 6, 5, 7, 6,\n      8, 9, 10, 9, 11, 10,\n      12, 13, 14, 13, 15, 14,\n    ];\n\n    let laserVertexBuffer = this._renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(laserVerts));\n    let laserIndexBuffer = this._renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(laserIndices));\n\n    let laserIndexCount = laserIndices.length;\n\n    let laserAttribs = [\n      new PrimitiveAttribute('POSITION', laserVertexBuffer, 3, gl.FLOAT, 20, 0),\n      new PrimitiveAttribute('TEXCOORD_0', laserVertexBuffer, 2, gl.FLOAT, 20, 12),\n    ];\n\n    let laserPrimitive = new Primitive(laserAttribs, laserIndexCount);\n    laserPrimitive.setIndexBuffer(laserIndexBuffer);\n\n    let laserMaterial = new LaserMaterial();\n\n    let laserRenderPrimitive = this._renderer.createRenderPrimitive(laserPrimitive, laserMaterial);\n    let meshNode = new Node();\n    meshNode.addRenderPrimitive(laserRenderPrimitive);\n    return meshNode;\n  }\n\n  _createCursorMesh() {\n    let gl = this._renderer._gl;\n\n    // Cursor is a circular white dot with a dark \"shadow\" skirt around the edge\n    // that fades from black to transparent as it moves out from the center.\n    // Cursor verts are packed as [X, Y, Luminance, Opacity]\n    let cursorVerts = [];\n    let cursorIndices = [];\n\n    let segRad = (2.0 * Math.PI) / CURSOR_SEGMENTS;\n\n    // Cursor center\n    for (let i = 0; i < CURSOR_SEGMENTS; ++i) {\n      let rad = i * segRad;\n      let x = Math.cos(rad);\n      let y = Math.sin(rad);\n      cursorVerts.push(x * CURSOR_RADIUS, y * CURSOR_RADIUS, 1.0, CURSOR_OPACITY);\n\n      if (i > 1) {\n        cursorIndices.push(0, i-1, i);\n      }\n    }\n\n    let indexOffset = CURSOR_SEGMENTS;\n\n    // Cursor Skirt\n    for (let i = 0; i < CURSOR_SEGMENTS; ++i) {\n      let rad = i * segRad;\n      let x = Math.cos(rad);\n      let y = Math.sin(rad);\n      cursorVerts.push(x * CURSOR_RADIUS, y * CURSOR_RADIUS,\n          CURSOR_SHADOW_INNER_LUMINANCE, CURSOR_SHADOW_INNER_OPACITY);\n      cursorVerts.push(x * CURSOR_SHADOW_RADIUS, y * CURSOR_SHADOW_RADIUS,\n          CURSOR_SHADOW_OUTER_LUMINANCE, CURSOR_SHADOW_OUTER_OPACITY);\n\n      if (i > 0) {\n        let idx = indexOffset + (i * 2);\n        cursorIndices.push(idx-2, idx-1, idx);\n        cursorIndices.push(idx-1, idx+1, idx);\n      }\n    }\n\n    let idx = indexOffset + (CURSOR_SEGMENTS * 2);\n    cursorIndices.push(idx-2, idx-1, indexOffset);\n    cursorIndices.push(idx-1, indexOffset+1, indexOffset);\n\n    let cursorVertexBuffer = this._renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(cursorVerts));\n    let cursorIndexBuffer = this._renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cursorIndices));\n\n    let cursorIndexCount = cursorIndices.length;\n\n    let cursorAttribs = [\n      new PrimitiveAttribute('POSITION', cursorVertexBuffer, 4, gl.FLOAT, 16, 0),\n    ];\n\n    let cursorPrimitive = new Primitive(cursorAttribs, cursorIndexCount);\n    cursorPrimitive.setIndexBuffer(cursorIndexBuffer);\n\n    let cursorMaterial = new CursorMaterial();\n    let cursorHiddenMaterial = new CursorHiddenMaterial();\n\n    // Cursor renders two parts: The bright opaque cursor for areas where it's\n    // not obscured and a more transparent, darker version for areas where it's\n    // behind another object.\n    let cursorRenderPrimitive = this._renderer.createRenderPrimitive(cursorPrimitive, cursorMaterial);\n    let cursorHiddenRenderPrimitive = this._renderer.createRenderPrimitive(cursorPrimitive, cursorHiddenMaterial);\n    let meshNode = new Node();\n    meshNode.addRenderPrimitive(cursorRenderPrimitive);\n    meshNode.addRenderPrimitive(cursorHiddenRenderPrimitive);\n    return meshNode;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nRenders simple text using a seven-segment LED style pattern. Only really good\nfor numbers and a limited number of other characters.\n*/\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\n\nconst TEXT_KERNING = 2.0;\n\nclass SevenSegmentMaterial extends Material {\n  get materialName() {\n    return 'SEVEN_SEGMENT_TEXT';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 0.0, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    precision mediump float;\n    const vec4 color = vec4(0.0, 1.0, 0.0, 1.0);\n\n    vec4 fragment_main() {\n      return color;\n    }`;\n  }\n}\n\nexport class SevenSegmentText extends Node {\n  constructor() {\n    super();\n\n    this._text = '';\n    this._charNodes = [];\n  }\n\n  onRendererChanged(renderer) {\n    this.clearNodes();\n    this._charNodes = [];\n\n    let vertices = [];\n    let segmentIndices = {};\n    let indices = [];\n\n    const width = 0.5;\n    const thickness = 0.25;\n\n    function defineSegment(id, left, top, right, bottom) {\n      let idx = vertices.length / 2;\n      vertices.push(\n          left, top,\n          right, top,\n          right, bottom,\n          left, bottom);\n\n      segmentIndices[id] = [\n        idx, idx+2, idx+1,\n        idx, idx+3, idx+2,\n      ];\n    }\n\n    let characters = {};\n    function defineCharacter(c, segments) {\n      let character = {\n        character: c,\n        offset: indices.length * 2,\n        count: 0,\n      };\n\n      for (let i = 0; i < segments.length; ++i) {\n        let idx = segments[i];\n        let segment = segmentIndices[idx];\n        character.count += segment.length;\n        indices.push(...segment);\n      }\n\n      characters[c] = character;\n    }\n\n    /* Segment layout is as follows:\n\n    |-0-|\n    3   4\n    |-1-|\n    5   6\n    |-2-|\n\n    */\n\n    defineSegment(0, -1, 1, width, 1-thickness);\n    defineSegment(1, -1, thickness*0.5, width, -thickness*0.5);\n    defineSegment(2, -1, -1+thickness, width, -1);\n    defineSegment(3, -1, 1, -1+thickness, -thickness*0.5);\n    defineSegment(4, width-thickness, 1, width, -thickness*0.5);\n    defineSegment(5, -1, thickness*0.5, -1+thickness, -1);\n    defineSegment(6, width-thickness, thickness*0.5, width, -1);\n\n\n    defineCharacter('0', [0, 2, 3, 4, 5, 6]);\n    defineCharacter('1', [4, 6]);\n    defineCharacter('2', [0, 1, 2, 4, 5]);\n    defineCharacter('3', [0, 1, 2, 4, 6]);\n    defineCharacter('4', [1, 3, 4, 6]);\n    defineCharacter('5', [0, 1, 2, 3, 6]);\n    defineCharacter('6', [0, 1, 2, 3, 5, 6]);\n    defineCharacter('7', [0, 4, 6]);\n    defineCharacter('8', [0, 1, 2, 3, 4, 5, 6]);\n    defineCharacter('9', [0, 1, 2, 3, 4, 6]);\n    defineCharacter('A', [0, 1, 3, 4, 5, 6]);\n    defineCharacter('B', [1, 2, 3, 5, 6]);\n    defineCharacter('C', [0, 2, 3, 5]);\n    defineCharacter('D', [1, 2, 4, 5, 6]);\n    defineCharacter('E', [0, 1, 2, 4, 6]);\n    defineCharacter('F', [0, 1, 3, 5]);\n    defineCharacter('P', [0, 1, 3, 4, 5]);\n    defineCharacter('-', [1]);\n    defineCharacter(' ', []);\n    defineCharacter('_', [2]); // Used for undefined characters\n\n    let gl = renderer.gl;\n    let vertexBuffer = renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(vertices));\n    let indexBuffer = renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));\n\n    let vertexAttribs = [\n      new PrimitiveAttribute('POSITION', vertexBuffer, 2, gl.FLOAT, 8, 0),\n    ];\n\n    let primitive = new Primitive(vertexAttribs, indices.length);\n    primitive.setIndexBuffer(indexBuffer);\n\n    let material = new SevenSegmentMaterial();\n\n    this._charPrimitives = {};\n    for (let char in characters) {\n      let charDef = characters[char];\n      primitive.elementCount = charDef.count;\n      primitive.indexByteOffset = charDef.offset;\n      this._charPrimitives[char] = renderer.createRenderPrimitive(primitive, material);\n    }\n\n    this.text = this._text;\n  }\n\n  get text() {\n    return this._text;\n  }\n\n  set text(value) {\n    this._text = value;\n\n    let i = 0;\n    let charPrimitive = null;\n    for (; i < value.length; ++i) {\n      if (value[i] in this._charPrimitives) {\n        charPrimitive = this._charPrimitives[value[i]];\n      } else {\n        charPrimitive = this._charPrimitives['_'];\n      }\n\n      if (this._charNodes.length <= i) {\n        let node = new Node();\n        node.addRenderPrimitive(charPrimitive);\n        let offset = i * TEXT_KERNING;\n        node.translation = [offset, 0, 0];\n        this._charNodes.push(node);\n        this.addNode(node);\n      } else {\n        // This is sort of an abuse of how these things are expected to work,\n        // but it's the cheapest thing I could think of that didn't break the\n        // world.\n        this._charNodes[i].clearRenderPrimitives();\n        this._charNodes[i].addRenderPrimitive(charPrimitive);\n        this._charNodes[i].visible = true;\n      }\n    }\n\n    // If there's any nodes left over make them invisible\n    for (; i < this._charNodes.length; ++i) {\n      this._charNodes[i].visible = false;\n    }\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nNode for displaying 360 equirect images as a skybox.\n*/\n\nimport {Material, RENDER_ORDER} from '../core/material.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {Node} from '../core/node.js';\nimport {UrlTexture} from '../core/texture.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nclass SkyboxMaterial extends Material {\n  constructor() {\n    super();\n    this.renderOrder = RENDER_ORDER.SKY;\n    this.state.depthFunc = GL.LEQUAL;\n    this.state.depthMask = false;\n\n    this.image = this.defineSampler('diffuse');\n\n    this.texCoordScaleOffset = this.defineUniform('texCoordScaleOffset',\n                                                      [1.0, 1.0, 0.0, 0.0,\n                                                       1.0, 1.0, 0.0, 0.0], 4);\n  }\n\n  get materialName() {\n    return 'SKYBOX';\n  }\n\n  get vertexSource() {\n    return `\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      // Drop the translation portion of the view matrix\n      view[3].xyz = vec3(0.0, 0.0, 0.0);\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n\n      // Returning the W component for both Z and W forces the geometry depth to\n      // the far plane. When combined with a depth func of LEQUAL this makes the\n      // sky write to any depth fragment that has not been written to yet.\n      return out_vec.xyww;\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }`;\n  }\n}\n\nexport class SkyboxNode extends Node {\n  constructor(options) {\n    super();\n\n    this._url = options.url;\n    this._displayMode = options.displayMode || 'mono';\n    this._rotationY = options.rotationY || 0;\n  }\n\n  onRendererChanged(renderer) {\n    let vertices = [];\n    let indices = [];\n\n    let latSegments = 40;\n    let lonSegments = 40;\n\n    // Create the vertices/indices\n    for (let i=0; i <= latSegments; ++i) {\n      let theta = i * Math.PI / latSegments;\n      let sinTheta = Math.sin(theta);\n      let cosTheta = Math.cos(theta);\n\n      let idxOffsetA = i * (lonSegments+1);\n      let idxOffsetB = (i+1) * (lonSegments+1);\n\n      for (let j=0; j <= lonSegments; ++j) {\n        let phi = (j * 2 * Math.PI / lonSegments) + this._rotationY;\n        let x = Math.sin(phi) * sinTheta;\n        let y = cosTheta;\n        let z = -Math.cos(phi) * sinTheta;\n        let u = (j / lonSegments);\n        let v = (i / latSegments);\n\n        // Vertex shader will force the geometry to the far plane, so the\n        // radius of the sphere is immaterial.\n        vertices.push(x, y, z, u, v);\n\n        if (i < latSegments && j < lonSegments) {\n          let idxA = idxOffsetA+j;\n          let idxB = idxOffsetB+j;\n\n          indices.push(idxA, idxB, idxA+1,\n                       idxB, idxB+1, idxA+1);\n        }\n      }\n    }\n\n    let vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices));\n    let indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));\n\n    let attribs = [\n      new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0),\n      new PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12),\n    ];\n\n    let primitive = new Primitive(attribs, indices.length);\n    primitive.setIndexBuffer(indexBuffer);\n\n    let material = new SkyboxMaterial();\n    material.image.texture = new UrlTexture(this._url);\n\n    switch (this._displayMode) {\n      case 'mono':\n        material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0,\n                                              1.0, 1.0, 0.0, 0.0];\n        break;\n      case 'stereoTopBottom':\n        material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0,\n                                              1.0, 0.5, 0.0, 0.5];\n        break;\n      case 'stereoLeftRight':\n        material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0,\n                                              0.5, 1.0, 0.5, 0.0];\n        break;\n    }\n\n    let renderPrimitive = renderer.createRenderPrimitive(primitive, material);\n    this.addRenderPrimitive(renderPrimitive);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nHeavily inspired by Mr. Doobs stats.js, this FPS counter is rendered completely\nwith WebGL, allowing it to be shown in cases where overlaid HTML elements aren't\nusable (like WebXR), or if you want the FPS counter to be rendered as part of\nyour scene.\n*/\n\nimport {Material} from '../core/material.js';\nimport {Node} from '../core/node.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {SevenSegmentText} from './seven-segment-text.js';\n\nconst SEGMENTS = 30;\nconst MAX_FPS = 90;\n\nclass StatsMaterial extends Material {\n  get materialName() {\n    return 'STATS_VIEWER';\n  }\n\n  get vertexSource() {\n    return `\n    attribute vec3 POSITION;\n    attribute vec3 COLOR_0;\n    varying vec4 vColor;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vColor = vec4(COLOR_0, 1.0);\n      return proj * view * model * vec4(POSITION, 1.0);\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    precision mediump float;\n    varying vec4 vColor;\n\n    vec4 fragment_main() {\n      return vColor;\n    }`;\n  }\n}\n\nfunction segmentToX(i) {\n  return ((0.9/SEGMENTS) * i) - 0.45;\n}\n\nfunction fpsToY(value) {\n  return (Math.min(value, MAX_FPS) * (0.7 / MAX_FPS)) - 0.45;\n}\n\nfunction fpsToRGB(value) {\n  return {\n    r: Math.max(0.0, Math.min(1.0, 1.0 - (value/60))),\n    g: Math.max(0.0, Math.min(1.0, ((value-15)/(MAX_FPS-15)))),\n    b: Math.max(0.0, Math.min(1.0, ((value-15)/(MAX_FPS-15)))),\n  };\n}\n\nlet now = (window.performance && performance.now) ? performance.now.bind(performance) : Date.now;\n\nexport class StatsViewer extends Node {\n  constructor() {\n    super();\n\n    this._performanceMonitoring = false;\n\n    this._startTime = now();\n    this._prevFrameTime = this._startTime;\n    this._prevGraphUpdateTime = this._startTime;\n    this._frames = 0;\n    this._fpsAverage = 0;\n    this._fpsMin = 0;\n    this._fpsStep = this._performanceMonitoring ? 1000 : 250;\n    this._lastSegment = 0;\n\n    this._fpsVertexBuffer = null;\n    this._fpsRenderPrimitive = null;\n    this._fpsNode = null;\n\n    this._sevenSegmentNode = new SevenSegmentText();\n    // Hard coded because it doesn't change:\n    // Scale by 0.075 in X and Y\n    // Translate into upper left corner w/ z = 0.02\n    this._sevenSegmentNode.matrix = new Float32Array([\n      0.075, 0, 0, 0,\n      0, 0.075, 0, 0,\n      0, 0, 1, 0,\n      -0.3625, 0.3625, 0.02, 1,\n    ]);\n  }\n\n  onRendererChanged(renderer) {\n    this.clearNodes();\n\n    let gl = renderer.gl;\n\n    let fpsVerts = [];\n    let fpsIndices = [];\n\n    // Graph geometry\n    for (let i = 0; i < SEGMENTS; ++i) {\n      // Bar top\n      fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0);\n      fpsVerts.push(segmentToX(i+1), fpsToY(0), 0.02, 0.0, 1.0, 1.0);\n\n      // Bar bottom\n      fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0);\n      fpsVerts.push(segmentToX(i+1), fpsToY(0), 0.02, 0.0, 1.0, 1.0);\n\n      let idx = i * 4;\n      fpsIndices.push(idx, idx+3, idx+1,\n                       idx+3, idx, idx+2);\n    }\n\n    function addBGSquare(left, bottom, right, top, z, r, g, b) {\n      let idx = fpsVerts.length / 6;\n\n      fpsVerts.push(left, bottom, z, r, g, b);\n      fpsVerts.push(right, top, z, r, g, b);\n      fpsVerts.push(left, top, z, r, g, b);\n      fpsVerts.push(right, bottom, z, r, g, b);\n\n      fpsIndices.push(idx, idx+1, idx+2,\n                       idx, idx+3, idx+1);\n    }\n\n    // Panel Background\n    addBGSquare(-0.5, -0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.125);\n\n    // FPS Background\n    addBGSquare(-0.45, -0.45, 0.45, 0.25, 0.01, 0.0, 0.0, 0.4);\n\n    // 30 FPS line\n    addBGSquare(-0.45, fpsToY(30), 0.45, fpsToY(32), 0.015, 0.5, 0.0, 0.5);\n\n    // 60 FPS line\n    addBGSquare(-0.45, fpsToY(60), 0.45, fpsToY(62), 0.015, 0.2, 0.0, 0.75);\n\n    this._fpsVertexBuffer = renderer.createRenderBuffer(gl.ARRAY_BUFFER, new Float32Array(fpsVerts), gl.DYNAMIC_DRAW);\n    let fpsIndexBuffer = renderer.createRenderBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(fpsIndices));\n\n    let fpsAttribs = [\n      new PrimitiveAttribute('POSITION', this._fpsVertexBuffer, 3, gl.FLOAT, 24, 0),\n      new PrimitiveAttribute('COLOR_0', this._fpsVertexBuffer, 3, gl.FLOAT, 24, 12),\n    ];\n\n    let fpsPrimitive = new Primitive(fpsAttribs, fpsIndices.length);\n    fpsPrimitive.setIndexBuffer(fpsIndexBuffer);\n    fpsPrimitive.setBounds([-0.5, -0.5, 0.0], [0.5, 0.5, 0.015]);\n\n    this._fpsRenderPrimitive = renderer.createRenderPrimitive(fpsPrimitive, new StatsMaterial());\n    this._fpsNode = new Node();\n    this._fpsNode.addRenderPrimitive(this._fpsRenderPrimitive);\n\n    this.addNode(this._fpsNode);\n    this.addNode(this._sevenSegmentNode);\n  }\n\n  get performanceMonitoring() {\n    return this._performanceMonitoring;\n  }\n\n  set performanceMonitoring(value) {\n    this._performanceMonitoring = value;\n    this._fpsStep = value ? 1000 : 250;\n  }\n\n  begin() {\n    this._startTime = now();\n  }\n\n  end() {\n    let time = now();\n\n    let frameFps = 1000 / (time - this._prevFrameTime);\n    this._prevFrameTime = time;\n    this._fpsMin = this._frames ? Math.min(this._fpsMin, frameFps) : frameFps;\n    this._frames++;\n\n    if (time > this._prevGraphUpdateTime + this._fpsStep) {\n      let intervalTime = time - this._prevGraphUpdateTime;\n      this._fpsAverage = Math.round(1000 / (intervalTime / this._frames));\n\n      // Draw both average and minimum FPS for this period\n      // so that dropped frames are more clearly visible.\n      this._updateGraph(this._fpsMin, this._fpsAverage);\n      if (this._performanceMonitoring) {\n        console.log(`Average FPS: ${this._fpsAverage} Min FPS: ${this._fpsMin}`);\n      }\n\n      this._prevGraphUpdateTime = time;\n      this._frames = 0;\n      this._fpsMin = 0;\n    }\n  }\n\n  _updateGraph(valueLow, valueHigh) {\n    let color = fpsToRGB(valueLow);\n    // Draw a range from the low to high value. Artificially widen the\n    // range a bit to ensure that near-equal values still remain\n    // visible - the logic here should match that used by the\n    // \"60 FPS line\" setup below. Hitting 60fps consistently will\n    // keep the top half of the 60fps background line visible.\n    let y0 = fpsToY(valueLow - 1);\n    let y1 = fpsToY(valueHigh + 1);\n\n    // Update the current segment with the new FPS value\n    let updateVerts = [\n      segmentToX(this._lastSegment), y1, 0.02, color.r, color.g, color.b,\n      segmentToX(this._lastSegment+1), y1, 0.02, color.r, color.g, color.b,\n      segmentToX(this._lastSegment), y0, 0.02, color.r, color.g, color.b,\n      segmentToX(this._lastSegment+1), y0, 0.02, color.r, color.g, color.b,\n    ];\n\n    // Re-shape the next segment into the green \"progress\" line\n    color.r = 0.2;\n    color.g = 1.0;\n    color.b = 0.2;\n\n    if (this._lastSegment == SEGMENTS - 1) {\n      // If we're updating the last segment we need to do two bufferSubDatas\n      // to update the segment and turn the first segment into the progress line.\n      this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts),\n                                        this._lastSegment * 24 * 4);\n      updateVerts = [\n        segmentToX(0), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b,\n        segmentToX(.25), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b,\n        segmentToX(0), fpsToY(0), 0.02, color.r, color.g, color.b,\n        segmentToX(.25), fpsToY(0), 0.02, color.r, color.g, color.b,\n      ];\n      this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts), 0);\n    } else {\n      updateVerts.push(\n        segmentToX(this._lastSegment+1), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b,\n        segmentToX(this._lastSegment+1.25), fpsToY(MAX_FPS), 0.02, color.r, color.g, color.b,\n        segmentToX(this._lastSegment+1), fpsToY(0), 0.02, color.r, color.g, color.b,\n        segmentToX(this._lastSegment+1.25), fpsToY(0), 0.02, color.r, color.g, color.b\n      );\n      this._renderer.updateRenderBuffer(this._fpsVertexBuffer, new Float32Array(updateVerts),\n                                        this._lastSegment * 24 * 4);\n    }\n\n    this._lastSegment = (this._lastSegment+1) % SEGMENTS;\n\n    this._sevenSegmentNode.text = `${this._fpsAverage} FP5`;\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nNode for displaying 2D or stereo videos on a quad.\n*/\n\nimport {Material} from '../core/material.js';\nimport {Primitive, PrimitiveAttribute} from '../core/primitive.js';\nimport {Node} from '../core/node.js';\nimport {VideoTexture} from '../core/texture.js';\n\nconst GL = WebGLRenderingContext; // For enums\n\nclass VideoMaterial extends Material {\n  constructor() {\n    super();\n\n    this.image = this.defineSampler('diffuse');\n\n    this.texCoordScaleOffset = this.defineUniform('texCoordScaleOffset',\n                                                      [1.0, 1.0, 0.0, 0.0,\n                                                       1.0, 1.0, 0.0, 0.0], 4);\n  }\n\n  get materialName() {\n    return 'VIDEO_PLAYER';\n  }\n\n  get vertexSource() {\n    return `\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n      return out_vec;\n    }`;\n  }\n\n  get fragmentSource() {\n    return `\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }`;\n  }\n}\n\nexport class VideoNode extends Node {\n  constructor(options) {\n    super();\n\n    this._video = options.video;\n    this._displayMode = options.displayMode || 'mono';\n\n    this._video_texture = new VideoTexture(this._video);\n  }\n\n  get aspectRatio() {\n    let width = this._video.videoWidth;\n    let height = this._video.videoHeight;\n\n    switch (this._displayMode) {\n      case 'stereoTopBottom': height *= 0.5; break;\n      case 'stereoLeftRight': width *= 0.5; break;\n    }\n\n    if (!height || !width) {\n      return 1;\n    }\n\n    return width / height;\n  }\n\n  onRendererChanged(renderer) {\n    let vertices = [\n      -1.0, 1.0, 0.0, 0.0, 0.0,\n       1.0, 1.0, 0.0, 1.0, 0.0,\n       1.0, -1.0, 0.0, 1.0, 1.0,\n      -1.0, -1.0, 0.0, 0.0, 1.0,\n    ];\n    let indices = [\n      0, 2, 1,\n      0, 3, 2,\n    ];\n\n    let vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices));\n    let indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices));\n\n    let attribs = [\n      new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0),\n      new PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12),\n    ];\n\n    let primitive = new Primitive(attribs, indices.length);\n    primitive.setIndexBuffer(indexBuffer);\n    primitive.setBounds([-1.0, -1.0, 0.0], [1.0, 1.0, 0.015]);\n\n    let material = new VideoMaterial();\n    material.image.texture = this._video_texture;\n\n    switch (this._displayMode) {\n      case 'mono':\n        material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0,\n                                                 1.0, 1.0, 0.0, 0.0];\n        break;\n      case 'stereoTopBottom':\n        material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0,\n                                                 1.0, 0.5, 0.0, 0.5];\n        break;\n      case 'stereoLeftRight':\n        material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0,\n                                                 0.5, 1.0, 0.5, 0.0];\n        break;\n    }\n\n    let renderPrimitive = renderer.createRenderPrimitive(primitive, material);\n    this.addRenderPrimitive(renderPrimitive);\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {RenderView} from '../core/renderer.js';\nimport {InputRenderer} from '../nodes/input-renderer.js';\nimport {StatsViewer} from '../nodes/stats-viewer.js';\nimport {Node} from '../core/node.js';\nimport {vec3, quat} from '../math/gl-matrix.js';\n\nexport class WebXRView extends RenderView {\n  constructor(view, layer) {\n    super(\n      view ? view.projectionMatrix : null,\n      view ? view.viewMatrix : null,\n      (layer && view) ? layer.getViewport(view) : null,\n      view ? view.eye : 'left'\n    );\n  }\n}\n\nexport class Scene extends Node {\n  constructor() {\n    super();\n\n    this._timestamp = -1;\n    this._frameDelta = 0;\n    this._statsStanding = false;\n    this._stats = null;\n    this._statsEnabled = false;\n    this.enableStats(true); // Ensure the stats are added correctly by default.\n\n    this._inputRenderer = null;\n    this._resetInputEndFrame = true;\n\n    this._lastTimestamp = 0;\n\n    this._hoverFrame = 0;\n    this._hoveredNodes = [];\n\n    this.clear = true;\n  }\n\n  setRenderer(renderer) {\n    this._setRenderer(renderer);\n  }\n\n  loseRenderer() {\n    if (this._renderer) {\n      this._stats = null;\n      this._renderer = null;\n      this._inputRenderer = null;\n    }\n  }\n\n  get inputRenderer() {\n    if (!this._inputRenderer) {\n      this._inputRenderer = new InputRenderer();\n      this.addNode(this._inputRenderer);\n    }\n    return this._inputRenderer;\n  }\n\n  // Helper function that automatically adds the appropriate visual elements for\n  // all input sources.\n  updateInputSources(frame, frameOfRef) {\n    // FIXME: Check for the existence of the API first. This check should be\n    // removed once the input API is part of the official spec.\n    if (!frame.session.getInputSources) {\n      return;\n    }\n\n    let inputSources = frame.session.getInputSources();\n\n    let newHoveredNodes = [];\n    let lastHoverFrame = this._hoverFrame;\n    this._hoverFrame++;\n\n    for (let inputSource of inputSources) {\n      let inputPose = frame.getInputPose(inputSource, frameOfRef);\n\n      if (!inputPose) {\n        continue;\n      }\n\n      // Any time that we have a grip matrix, we'll render a controller.\n      if (inputPose.gripMatrix) {\n        this.inputRenderer.addController(inputPose.gripMatrix);\n      }\n\n      if (inputPose.targetRay) {\n        if (inputSource.targetRayMode == 'tracked-pointer') {\n          // If we have a pointer matrix and the pointer origin is the users\n          // hand (as opposed to their head or the screen) use it to render\n          // a ray coming out of the input device to indicate the pointer\n          // direction.\n          this.inputRenderer.addLaserPointer(inputPose.targetRay);\n        }\n\n        // If we have a pointer matrix we can also use it to render a cursor\n        // for both handheld and gaze-based input sources.\n\n        // Check and see if the pointer is pointing at any selectable objects.\n        let hitResult = this.hitTest(inputPose.targetRay);\n\n        if (hitResult) {\n          // Render a cursor at the intersection point.\n          this.inputRenderer.addCursor(hitResult.intersection);\n\n          if (hitResult.node._hoverFrameId != lastHoverFrame) {\n            hitResult.node.onHoverStart();\n          }\n          hitResult.node._hoverFrameId = this._hoverFrame;\n          newHoveredNodes.push(hitResult.node);\n        } else {\n          // Statically render the cursor 1 meters down the ray since we didn't\n          // hit anything selectable.\n          let cursorDistance = 1.0;\n          let cursorPos = vec3.fromValues(\n              inputPose.targetRay.origin.x,\n              inputPose.targetRay.origin.y,\n              inputPose.targetRay.origin.z\n              );\n          vec3.add(cursorPos, cursorPos, [\n              inputPose.targetRay.direction.x * cursorDistance,\n              inputPose.targetRay.direction.y * cursorDistance,\n              inputPose.targetRay.direction.z * cursorDistance,\n              ]);\n          // let cursorPos = vec3.fromValues(0, 0, -1.0);\n          // vec3.transformMat4(cursorPos, cursorPos, inputPose.targetRay);\n          this.inputRenderer.addCursor(cursorPos);\n        }\n      }\n    }\n\n    for (let hoverNode of this._hoveredNodes) {\n      if (hoverNode._hoverFrameId != this._hoverFrame) {\n        hoverNode.onHoverEnd();\n      }\n    }\n\n    this._hoveredNodes = newHoveredNodes;\n  }\n\n  handleSelect(inputSource, frame, frameOfRef) {\n    let inputPose = frame.getInputPose(inputSource, frameOfRef);\n\n    if (!inputPose) {\n      return;\n    }\n\n    this.handleSelectPointer(inputPose.targetRay);\n  }\n\n  handleSelectPointer(targetRay) {\n    if (targetRay) {\n      // Check and see if the pointer is pointing at any selectable objects.\n      let hitResult = this.hitTest(targetRay);\n\n      if (hitResult) {\n        // Render a cursor at the intersection point.\n        hitResult.node.handleSelect();\n      }\n    }\n  }\n\n  enableStats(enable) {\n    if (enable == this._statsEnabled) {\n      return;\n    }\n\n    this._statsEnabled = enable;\n\n    if (enable) {\n      this._stats = new StatsViewer();\n      this._stats.selectable = true;\n      this.addNode(this._stats);\n\n      if (this._statsStanding) {\n        this._stats.translation = [0, 1.4, -0.75];\n      } else {\n        this._stats.translation = [0, -0.3, -0.5];\n      }\n      this._stats.scale = [0.3, 0.3, 0.3];\n      quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);\n    } else if (!enable) {\n      if (this._stats) {\n        this.removeNode(this._stats);\n        this._stats = null;\n      }\n    }\n  }\n\n  standingStats(enable) {\n    this._statsStanding = enable;\n    if (this._stats) {\n      if (this._statsStanding) {\n        this._stats.translation = [0, 1.4, -0.75];\n      } else {\n        this._stats.translation = [0, -0.3, -0.5];\n      }\n      this._stats.scale = [0.3, 0.3, 0.3];\n      quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);\n    }\n  }\n\n  draw(projectionMatrix, viewMatrix, eye) {\n    let view = new RenderView();\n    view.projectionMatrix = projectionMatrix;\n    view.viewMatrix = viewMatrix;\n    if (eye) {\n      view.eye = eye;\n    }\n\n    this.drawViewArray([view]);\n  }\n\n  /** Draws the scene into the base layer of the XRFrame's session */\n  drawXRFrame(xrFrame, pose) {\n    if (!this._renderer || !pose) {\n      return;\n    }\n\n    let gl = this._renderer.gl;\n    let session = xrFrame.session;\n    // Assumed to be a XRWebGLLayer for now.\n    let layer = session.baseLayer;\n\n    if (!gl) {\n      return;\n    }\n\n    gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);\n\n    if (this.clear) {\n      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n    }\n\n    let views = [];\n    for (let view of pose.views) {\n      views.push(new WebXRView(view, layer));\n    }\n\n    this.drawViewArray(views);\n  }\n\n  drawViewArray(views) {\n    // Don't draw when we don't have a valid context\n    if (!this._renderer) {\n      return;\n    }\n\n    this._renderer.drawViews(views, this);\n  }\n\n  startFrame() {\n    let prevTimestamp = this._timestamp;\n    this._timestamp = performance.now();\n    if (this._stats) {\n      this._stats.begin();\n    }\n\n    if (prevTimestamp >= 0) {\n      this._frameDelta = this._timestamp - prevTimestamp;\n    } else {\n      this._frameDelta = 0;\n    }\n\n    this._update(this._timestamp, this._frameDelta);\n\n    return this._frameDelta;\n  }\n\n  endFrame() {\n    if (this._inputRenderer && this._resetInputEndFrame) {\n      this._inputRenderer.reset();\n    }\n\n    if (this._stats) {\n      this._stats.end();\n    }\n  }\n\n  // Override to load scene resources on construction or context restore.\n  onLoadScene(renderer) {\n    return Promise.resolve();\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport {mat4} from '../math/gl-matrix.js';\n\nconst LOOK_SPEED = 0.0025;\n\nexport class FallbackHelper {\n  constructor(scene, gl) {\n    this.scene = scene;\n    this.gl = gl;\n    this._emulateStage = false;\n\n    this.lookYaw = 0;\n    this.lookPitch = 0;\n\n    this.viewMatrix = mat4.create();\n\n    let projectionMatrix = mat4.create();\n    this.projectionMatrix = projectionMatrix;\n\n    // Using a simple identity matrix for the view.\n    mat4.identity(this.viewMatrix);\n\n    // We need to track the canvas size in order to resize the WebGL\n    // backbuffer width and height, as well as update the projection matrix\n    // and adjust the viewport.\n    function onResize() {\n      gl.canvas.width = gl.canvas.offsetWidth * window.devicePixelRatio;\n      gl.canvas.height = gl.canvas.offsetHeight * window.devicePixelRatio;\n      mat4.perspective(projectionMatrix, Math.PI*0.4,\n                       gl.canvas.width/gl.canvas.height,\n                       0.1, 1000.0);\n      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n    }\n    window.addEventListener('resize', onResize);\n    onResize();\n\n    // Upding the view matrix with touch or mouse events.\n    let canvas = gl.canvas;\n    let lastTouchX = 0;\n    let lastTouchY = 0;\n    canvas.addEventListener('touchstart', (ev) => {\n      if (ev.touches.length == 2) {\n        lastTouchX = ev.touches[1].pageX;\n        lastTouchY = ev.touches[1].pageY;\n      }\n    });\n    canvas.addEventListener('touchmove', (ev) => {\n      // Rotate the view when two fingers are being used.\n      if (ev.touches.length == 2) {\n        this.onLook(ev.touches[1].pageX - lastTouchX, ev.touches[1].pageY - lastTouchY);\n        lastTouchX = ev.touches[1].pageX;\n        lastTouchY = ev.touches[1].pageY;\n      }\n    });\n    canvas.addEventListener('mousemove', (ev) => {\n      // Only rotate when the right button is pressed.\n      if (ev.buttons & 2) {\n        this.onLook(ev.movementX, ev.movementY);\n      }\n    });\n    canvas.addEventListener('contextmenu', (ev) => {\n      // Prevent context menus on the canvas so that we can use right click to rotate.\n      ev.preventDefault();\n    });\n\n    this.boundOnFrame = this.onFrame.bind(this);\n    window.requestAnimationFrame(this.boundOnFrame);\n  }\n\n  onLook(yaw, pitch) {\n    this.lookYaw += yaw * LOOK_SPEED;\n    this.lookPitch += pitch * LOOK_SPEED;\n\n    // Clamp pitch rotation beyond looking straight up or down.\n    if (this.lookPitch < -Math.PI*0.5) {\n        this.lookPitch = -Math.PI*0.5;\n    }\n    if (this.lookPitch > Math.PI*0.5) {\n        this.lookPitch = Math.PI*0.5;\n    }\n\n    this.updateView();\n  }\n\n  onFrame(t) {\n    let gl = this.gl;\n    window.requestAnimationFrame(this.boundOnFrame);\n\n    this.scene.startFrame();\n\n    // We can skip setting the framebuffer and viewport every frame, because\n    // it won't change from frame to frame and we're updating the viewport\n    // only when we resize for efficency.\n    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n\n    // We're drawing with our own projection and view matrix now, and we\n    // don't have a list of view to loop through, but otherwise all of the\n    // WebGL drawing logic is exactly the same.\n    this.scene.draw(this.projectionMatrix, this.viewMatrix);\n\n    this.scene.endFrame();\n  }\n\n  get emulateStage() {\n    return this._emulateStage;\n  }\n\n  set emulateStage(value) {\n    this._emulateStage = value;\n    this.updateView();\n  }\n\n  updateView() {\n    mat4.identity(this.viewMatrix);\n\n    mat4.rotateX(this.viewMatrix, this.viewMatrix, -this.lookPitch);\n    mat4.rotateY(this.viewMatrix, this.viewMatrix, -this.lookYaw);\n\n    // If we're emulating a stage frame of reference we'll need to move the view\n    // matrix roughly a meter and a half up in the air.\n    if (this._emulateStage) {\n      mat4.translate(this.viewMatrix, this.viewMatrix, [0, -1.6, 0]);\n    }\n  }\n}\n","// Copyright 2018 The Immersive Web Community Group\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/*\nProvides a simple way to get values from the query string if they're present\nand use a default value if not. Not strictly a \"WebGL\" utility, but I use it\nfrequently enough for debugging that I wanted to include it here.\n\nExample:\nFor the URL http://example.com/index.html?particleCount=1000\n\nQueryArgs.getInt(\"particleCount\", 100); // URL overrides, returns 1000\nQueryArgs.getInt(\"particleSize\", 10); // Not in URL, returns default of 10\n*/\n\nlet urlArgs = null;\nwindow.onhashchange = function() {\n  // Force re-parsing on next access\n  urlArgs = null;\n};\n\nfunction ensureArgsCached() {\n  if (!urlArgs) {\n    urlArgs = {};\n    let query = window.location.search.substring(1) || window.location.hash.substring(1);\n    let vars = query.split('&');\n    for (let i = 0; i < vars.length; i++) {\n      let pair = vars[i].split('=');\n      urlArgs[pair[0].toLowerCase()] = decodeURIComponent(pair[1]);\n    }\n  }\n}\n\nexport class QueryArgs {\n  static getString(name, defaultValue) {\n    ensureArgsCached();\n    let lcaseName = name.toLowerCase();\n    if (lcaseName in urlArgs) {\n      return urlArgs[lcaseName];\n    }\n    return defaultValue;\n  }\n\n  static getInt(name, defaultValue) {\n    ensureArgsCached();\n    let lcaseName = name.toLowerCase();\n    if (lcaseName in urlArgs) {\n      return parseInt(urlArgs[lcaseName], 10);\n    }\n    return defaultValue;\n  }\n\n  static getFloat(name, defaultValue) {\n    ensureArgsCached();\n    let lcaseName = name.toLowerCase();\n    if (lcaseName in urlArgs) {\n      return parseFloat(urlArgs[lcaseName]);\n    }\n    return defaultValue;\n  }\n\n  static getBool(name, defaultValue) {\n    ensureArgsCached();\n    let lcaseName = name.toLowerCase();\n    if (lcaseName in urlArgs) {\n      return parseInt(urlArgs[lcaseName], 10) != 0;\n    }\n    return defaultValue;\n  }\n}\n"],"sourceRoot":""}
\ No newline at end of file
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.js
deleted file mode 100644
index a7a02da..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/build/cottontail.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*!
- * Copyright 2018 The Immersive Web Community Group
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- * 
- */
-!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var r=e();for(var n in r)("object"==typeof exports?exports:t)[n]=r[n]}}(window,function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)r.d(n,i,function(e){return t[e]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=15)}([function(t,e,r){"use strict";r.r(e),r.d(e,"EPSILON",function(){return n}),r.d(e,"ARRAY_TYPE",function(){return i}),r.d(e,"RANDOM",function(){return o}),r.d(e,"setMatrixArrayType",function(){return a}),r.d(e,"toRadian",function(){return s}),r.d(e,"equals",function(){return c});const n=1e-6;let i="undefined"!=typeof Float32Array?Float32Array:Array;const o=Math.random;function a(t){i=t}const u=Math.PI/180;function s(t){return t*u}function c(t,e){return Math.abs(t-e)<=n*Math.max(1,Math.abs(t),Math.abs(e))}},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"fromValues",function(){return a}),r.d(e,"copy",function(){return u}),r.d(e,"set",function(){return s}),r.d(e,"add",function(){return c}),r.d(e,"subtract",function(){return f}),r.d(e,"multiply",function(){return l}),r.d(e,"divide",function(){return h}),r.d(e,"ceil",function(){return d}),r.d(e,"floor",function(){return v}),r.d(e,"min",function(){return _}),r.d(e,"max",function(){return m}),r.d(e,"round",function(){return p}),r.d(e,"scale",function(){return y}),r.d(e,"scaleAndAdd",function(){return b}),r.d(e,"distance",function(){return g}),r.d(e,"squaredDistance",function(){return M}),r.d(e,"length",function(){return x}),r.d(e,"squaredLength",function(){return T}),r.d(e,"negate",function(){return E}),r.d(e,"inverse",function(){return w}),r.d(e,"normalize",function(){return O}),r.d(e,"dot",function(){return R}),r.d(e,"lerp",function(){return P}),r.d(e,"random",function(){return S}),r.d(e,"transformMat4",function(){return A}),r.d(e,"transformQuat",function(){return N}),r.d(e,"str",function(){return C}),r.d(e,"exactEquals",function(){return I}),r.d(e,"equals",function(){return L}),r.d(e,"sub",function(){return k}),r.d(e,"mul",function(){return F}),r.d(e,"div",function(){return D}),r.d(e,"dist",function(){return j}),r.d(e,"sqrDist",function(){return B}),r.d(e,"len",function(){return U}),r.d(e,"sqrLen",function(){return V}),r.d(e,"forEach",function(){return Y});var n=r(0);function i(){let t=new n.ARRAY_TYPE(4);return n.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function o(t){let e=new n.ARRAY_TYPE(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}function a(t,e,r,i){let o=new n.ARRAY_TYPE(4);return o[0]=t,o[1]=e,o[2]=r,o[3]=i,o}function u(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function s(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}function c(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t}function f(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t}function l(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t[3]=e[3]*r[3],t}function h(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t[3]=e[3]/r[3],t}function d(t,e){return t[0]=Math.ceil(e[0]),t[1]=Math.ceil(e[1]),t[2]=Math.ceil(e[2]),t[3]=Math.ceil(e[3]),t}function v(t,e){return t[0]=Math.floor(e[0]),t[1]=Math.floor(e[1]),t[2]=Math.floor(e[2]),t[3]=Math.floor(e[3]),t}function _(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t[3]=Math.min(e[3],r[3]),t}function m(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t[3]=Math.max(e[3],r[3]),t}function p(t,e){return t[0]=Math.round(e[0]),t[1]=Math.round(e[1]),t[2]=Math.round(e[2]),t[3]=Math.round(e[3]),t}function y(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t}function b(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t}function g(t,e){let r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],o=e[3]-t[3];return Math.sqrt(r*r+n*n+i*i+o*o)}function M(t,e){let r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],o=e[3]-t[3];return r*r+n*n+i*i+o*o}function x(t){let e=t[0],r=t[1],n=t[2],i=t[3];return Math.sqrt(e*e+r*r+n*n+i*i)}function T(t){let e=t[0],r=t[1],n=t[2],i=t[3];return e*e+r*r+n*n+i*i}function E(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t}function w(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t[3]=1/e[3],t}function O(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=r*r+n*n+i*i+o*o;return a>0&&(a=1/Math.sqrt(a),t[0]=r*a,t[1]=n*a,t[2]=i*a,t[3]=o*a),t}function R(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]}function P(t,e,r,n){let i=e[0],o=e[1],a=e[2],u=e[3];return t[0]=i+n*(r[0]-i),t[1]=o+n*(r[1]-o),t[2]=a+n*(r[2]-a),t[3]=u+n*(r[3]-u),t}function S(t,e){var r,i,o,a,u,s;e=e||1;do{u=(r=2*n.RANDOM()-1)*r+(i=2*n.RANDOM()-1)*i}while(u>=1);do{s=(o=2*n.RANDOM()-1)*o+(a=2*n.RANDOM()-1)*a}while(s>=1);var c=Math.sqrt((1-u)/s);return t[0]=e*r,t[1]=e*i,t[2]=e*o*c,t[3]=e*a*c,t}function A(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*o+r[12]*a,t[1]=r[1]*n+r[5]*i+r[9]*o+r[13]*a,t[2]=r[2]*n+r[6]*i+r[10]*o+r[14]*a,t[3]=r[3]*n+r[7]*i+r[11]*o+r[15]*a,t}function N(t,e,r){let n=e[0],i=e[1],o=e[2],a=r[0],u=r[1],s=r[2],c=r[3],f=c*n+u*o-s*i,l=c*i+s*n-a*o,h=c*o+a*i-u*n,d=-a*n-u*i-s*o;return t[0]=f*c+d*-a+l*-s-h*-u,t[1]=l*c+d*-u+h*-a-f*-s,t[2]=h*c+d*-s+f*-u-l*-a,t[3]=e[3],t}function C(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"}function I(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]}function L(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=e[0],s=e[1],c=e[2],f=e[3];return Math.abs(r-u)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(i-s)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(s))&&Math.abs(o-c)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(c))&&Math.abs(a-f)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(f))}const k=f,F=l,D=h,j=g,B=M,U=x,V=T,Y=function(){let t=i();return function(e,r,n,i,o,a){let u,s;for(r||(r=4),n||(n=0),s=i?Math.min(i*r+n,e.length):e.length,u=n;u<s;u+=r)t[0]=e[u],t[1]=e[u+1],t[2]=e[u+2],t[3]=e[u+3],o(t,t,a),e[u]=t[0],e[u+1]=t[1],e[u+2]=t[2],e[u+3]=t[3];return e}}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Node=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(16),o=r(6);var a=new Float32Array([0,0,0]),u=new Float32Array([0,0,0,1]),s=new Float32Array([1,1,1]),c=o.mat4.create();e.Node=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.name=null,this.children=[],this.parent=null,this.visible=!0,this.selectable=!1,this._matrix=null,this._dirtyTRS=!1,this._translation=null,this._rotation=null,this._scale=null,this._dirtyWorldMatrix=!1,this._worldMatrix=null,this._activeFrameId=-1,this._hoverFrameId=-1,this._renderPrimitives=null,this._renderer=null,this._selectHandler=null}return n(t,[{key:"_setRenderer",value:function(t){if(this._renderer!=t&&(this._renderer&&this.clearRenderPrimitives(),this._renderer=t,t)){this.onRendererChanged(t);var e=!0,r=!1,n=void 0;try{for(var i,o=this.children[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){i.value._setRenderer(t)}}catch(t){r=!0,n=t}finally{try{!e&&o.return&&o.return()}finally{if(r)throw n}}}}},{key:"onRendererChanged",value:function(t){}},{key:"clone",value:function(){var e=this,r=new t;return r.name=this.name,r.visible=this.visible,r._renderer=this._renderer,r._dirtyTRS=this._dirtyTRS,this._translation&&(r._translation=o.vec3.create(),o.vec3.copy(r._translation,this._translation)),this._rotation&&(r._rotation=o.quat.create(),o.quat.copy(r._rotation,this._rotation)),this._scale&&(r._scale=o.vec3.create(),o.vec3.copy(r._scale,this._scale)),!r._dirtyTRS&&this._matrix&&(r._matrix=o.mat4.create(),o.mat4.copy(r._matrix,this._matrix)),r._dirtyWorldMatrix=this._dirtyWorldMatrix,!r._dirtyWorldMatrix&&this._worldMatrix&&(r._worldMatrix=o.mat4.create(),o.mat4.copy(r._worldMatrix,this._worldMatrix)),this.waitForComplete().then(function(){if(e._renderPrimitives){var t=!0,n=!1,i=void 0;try{for(var o,a=e._renderPrimitives[Symbol.iterator]();!(t=(o=a.next()).done);t=!0){var u=o.value;r.addRenderPrimitive(u)}}catch(t){n=!0,i=t}finally{try{!t&&a.return&&a.return()}finally{if(n)throw i}}}var s=!0,c=!1,f=void 0;try{for(var l,h=e.children[Symbol.iterator]();!(s=(l=h.next()).done);s=!0){var d=l.value;r.addNode(d.clone())}}catch(t){c=!0,f=t}finally{try{!s&&h.return&&h.return()}finally{if(c)throw f}}}),r}},{key:"markActive",value:function(t){if(this.visible&&this._renderPrimitives){this._activeFrameId=t;var e=!0,r=!1,n=void 0;try{for(var i,o=this._renderPrimitives[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){i.value.markActive(t)}}catch(t){r=!0,n=t}finally{try{!e&&o.return&&o.return()}finally{if(r)throw n}}}var a=!0,u=!1,s=void 0;try{for(var c,f=this.children[Symbol.iterator]();!(a=(c=f.next()).done);a=!0){var l=c.value;l.visible&&l.markActive(t)}}catch(t){u=!0,s=t}finally{try{!a&&f.return&&f.return()}finally{if(u)throw s}}}},{key:"addNode",value:function(t){t&&t.parent!=this&&(t.parent&&t.parent.removeNode(t),t.parent=this,this.children.push(t),this._renderer&&t._setRenderer(this._renderer))}},{key:"removeNode",value:function(t){var e=this.children.indexOf(t);e>-1&&(this.children.splice(e,1),t.parent=null)}},{key:"clearNodes",value:function(){var t=!0,e=!1,r=void 0;try{for(var n,i=this.children[Symbol.iterator]();!(t=(n=i.next()).done);t=!0){n.value.parent=null}}catch(t){e=!0,r=t}finally{try{!t&&i.return&&i.return()}finally{if(e)throw r}}this.children=[]}},{key:"setMatrixDirty",value:function(){if(!this._dirtyWorldMatrix){this._dirtyWorldMatrix=!0;var t=!0,e=!1,r=void 0;try{for(var n,i=this.children[Symbol.iterator]();!(t=(n=i.next()).done);t=!0){n.value.setMatrixDirty()}}catch(t){e=!0,r=t}finally{try{!t&&i.return&&i.return()}finally{if(e)throw r}}}}},{key:"_updateLocalMatrix",value:function(){return this._matrix||(this._matrix=o.mat4.create()),this._dirtyTRS&&(this._dirtyTRS=!1,o.mat4.fromRotationTranslationScale(this._matrix,this._rotation||u,this._translation||a,this._scale||s)),this._matrix}},{key:"waitForComplete",value:function(){var t=this,e=[],r=!0,n=!1,i=void 0;try{for(var o,a=this.children[Symbol.iterator]();!(r=(o=a.next()).done);r=!0){var u=o.value;e.push(u.waitForComplete())}}catch(t){n=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(n)throw i}}if(this._renderPrimitives){var s=!0,c=!1,f=void 0;try{for(var l,h=this._renderPrimitives[Symbol.iterator]();!(s=(l=h.next()).done);s=!0){var d=l.value;e.push(d.waitForComplete())}}catch(t){c=!0,f=t}finally{try{!s&&h.return&&h.return()}finally{if(c)throw f}}}return Promise.all(e).then(function(){return t})}},{key:"addRenderPrimitive",value:function(t){this._renderPrimitives?this._renderPrimitives.push(t):this._renderPrimitives=[t],t._instances.push(this)}},{key:"removeRenderPrimitive",value:function(t){if(this._renderPrimitives){var e=this._renderPrimitives._instances.indexOf(t);e>-1&&(this._renderPrimitives._instances.splice(e,1),(e=t._instances.indexOf(this))>-1&&t._instances.splice(e,1),this._renderPrimitives.length||(this._renderPrimitives=null))}}},{key:"clearRenderPrimitives",value:function(){if(this._renderPrimitives){var t=!0,e=!1,r=void 0;try{for(var n,i=this._renderPrimitives[Symbol.iterator]();!(t=(n=i.next()).done);t=!0){var o=n.value,a=o._instances.indexOf(this);a>-1&&o._instances.splice(a,1)}}catch(t){e=!0,r=t}finally{try{!t&&i.return&&i.return()}finally{if(e)throw r}}this._renderPrimitives=null}}},{key:"_hitTestSelectableNode",value:function(t){if(this._renderPrimitives){var e=null,r=!0,n=!1,a=void 0;try{for(var u,s=this._renderPrimitives[Symbol.iterator]();!(r=(u=s.next()).done);r=!0){var f=u.value;if(f._min){e||(o.mat4.invert(c,this.worldMatrix),o.mat4.multiply(c,c,t.transformMatrix),e=new i.Ray(c));var l=e.intersectsAABB(f._min,f._max);if(l)return o.vec3.transformMat4(l,l,this.worldMatrix),l}}}catch(t){n=!0,a=t}finally{try{!r&&s.return&&s.return()}finally{if(n)throw a}}}var h=!0,d=!1,v=void 0;try{for(var _,m=this.children[Symbol.iterator]();!(h=(_=m.next()).done);h=!0){var p=_.value._hitTestSelectableNode(t);if(p)return p}}catch(t){d=!0,v=t}finally{try{!h&&m.return&&m.return()}finally{if(d)throw v}}return null}},{key:"hitTest",value:function(t){if(this.selectable&&this.visible){var e=this._hitTestSelectableNode(t);if(e){var r=o.vec3.fromValues(t.origin.x,t.origin.y,t.origin.z);return{node:this,intersection:e,distance:o.vec3.distance(r,e)}}return null}var n=null,i=!0,a=!1,u=void 0;try{for(var s,c=this.children[Symbol.iterator]();!(i=(s=c.next()).done);i=!0){var f=s.value.hitTest(t);f&&(!n||n.distance>f.distance)&&(n=f)}}catch(t){a=!0,u=t}finally{try{!i&&c.return&&c.return()}finally{if(a)throw u}}return n}},{key:"onSelect",value:function(t){this._selectHandler=t}},{key:"handleSelect",value:function(){this._selectHandler&&this._selectHandler()}},{key:"onHoverStart",value:function(){}},{key:"onHoverEnd",value:function(){}},{key:"_update",value:function(t,e){this.onUpdate(t,e);var r=!0,n=!1,i=void 0;try{for(var o,a=this.children[Symbol.iterator]();!(r=(o=a.next()).done);r=!0){o.value._update(t,e)}}catch(t){n=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(n)throw i}}}},{key:"onUpdate",value:function(t,e){}},{key:"matrix",set:function(t){t?(this._matrix||(this._matrix=o.mat4.create()),o.mat4.copy(this._matrix,t)):this._matrix=null,this.setMatrixDirty(),this._dirtyTRS=!1,this._translation=null,this._rotation=null,this._scale=null},get:function(){return this.setMatrixDirty(),this._updateLocalMatrix()}},{key:"worldMatrix",get:function(){return this._worldMatrix||(this._dirtyWorldMatrix=!0,this._worldMatrix=o.mat4.create()),(this._dirtyWorldMatrix||this._dirtyTRS)&&(this.parent?o.mat4.mul(this._worldMatrix,this.parent.worldMatrix,this._updateLocalMatrix()):o.mat4.copy(this._worldMatrix,this._updateLocalMatrix()),this._dirtyWorldMatrix=!1),this._worldMatrix}},{key:"translation",set:function(t){null!=t&&(this._dirtyTRS=!0,this.setMatrixDirty()),this._translation=t},get:function(){return this._dirtyTRS=!0,this.setMatrixDirty(),this._translation||(this._translation=o.vec3.clone(a)),this._translation}},{key:"rotation",set:function(t){null!=t&&(this._dirtyTRS=!0,this.setMatrixDirty()),this._rotation=t},get:function(){return this._dirtyTRS=!0,this.setMatrixDirty(),this._rotation||(this._rotation=o.quat.clone(u)),this._rotation}},{key:"scale",set:function(t){null!=t&&(this._dirtyTRS=!0,this.setMatrixDirty()),this._scale=t},get:function(){return this._dirtyTRS=!0,this.setMatrixDirty(),this._scale||(this._scale=o.vec3.clone(s)),this._scale}},{key:"renderPrimitives",get:function(){return this._renderPrimitives}},{key:"selectHandler",get:function(){return this._selectHandler}}]),t}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.stateToBlendFunc=c;var o=WebGLRenderingContext,a=e.CAP={CULL_FACE:1,BLEND:2,DEPTH_TEST:4,STENCIL_TEST:8,COLOR_MASK:16,DEPTH_MASK:32,STENCIL_MASK:64},u=e.MAT_STATE={CAPS_RANGE:255,BLEND_SRC_SHIFT:8,BLEND_SRC_RANGE:3840,BLEND_DST_SHIFT:12,BLEND_DST_RANGE:61440,BLEND_FUNC_RANGE:65280,DEPTH_FUNC_SHIFT:16,DEPTH_FUNC_RANGE:983040},s=e.RENDER_ORDER={OPAQUE:0,SKY:1,TRANSPARENT:2,ADDITIVE:3,DEFAULT:4};function c(t,e,r){var n=(t&e)>>r;switch(n){case 0:case 1:return n;default:return n-2+o.SRC_COLOR}}var f=e.MaterialState=function(){function t(){i(this,t),this._state=a.CULL_FACE|a.DEPTH_TEST|a.COLOR_MASK|a.DEPTH_MASK,this.blendFuncSrc=o.SRC_ALPHA,this.blendFuncDst=o.ONE_MINUS_SRC_ALPHA,this.depthFunc=o.LESS}return n(t,[{key:"cullFace",get:function(){return!!(this._state&a.CULL_FACE)},set:function(t){t?this._state|=a.CULL_FACE:this._state&=~a.CULL_FACE}},{key:"blend",get:function(){return!!(this._state&a.BLEND)},set:function(t){t?this._state|=a.BLEND:this._state&=~a.BLEND}},{key:"depthTest",get:function(){return!!(this._state&a.DEPTH_TEST)},set:function(t){t?this._state|=a.DEPTH_TEST:this._state&=~a.DEPTH_TEST}},{key:"stencilTest",get:function(){return!!(this._state&a.STENCIL_TEST)},set:function(t){t?this._state|=a.STENCIL_TEST:this._state&=~a.STENCIL_TEST}},{key:"colorMask",get:function(){return!!(this._state&a.COLOR_MASK)},set:function(t){t?this._state|=a.COLOR_MASK:this._state&=~a.COLOR_MASK}},{key:"depthMask",get:function(){return!!(this._state&a.DEPTH_MASK)},set:function(t){t?this._state|=a.DEPTH_MASK:this._state&=~a.DEPTH_MASK}},{key:"depthFunc",get:function(){return((this._state&u.DEPTH_FUNC_RANGE)>>u.DEPTH_FUNC_SHIFT)+o.NEVER},set:function(t){t-=o.NEVER,this._state&=~u.DEPTH_FUNC_RANGE,this._state|=t<<u.DEPTH_FUNC_SHIFT}},{key:"stencilMask",get:function(){return!!(this._state&a.STENCIL_MASK)},set:function(t){t?this._state|=a.STENCIL_MASK:this._state&=~a.STENCIL_MASK}},{key:"blendFuncSrc",get:function(){return c(this._state,u.BLEND_SRC_RANGE,u.BLEND_SRC_SHIFT)},set:function(t){switch(t){case 0:case 1:break;default:t=t-o.SRC_COLOR+2}this._state&=~u.BLEND_SRC_RANGE,this._state|=t<<u.BLEND_SRC_SHIFT}},{key:"blendFuncDst",get:function(){return c(this._state,u.BLEND_DST_RANGE,u.BLEND_DST_SHIFT)},set:function(t){switch(t){case 0:case 1:break;default:t=t-o.SRC_COLOR+2}this._state&=~u.BLEND_DST_RANGE,this._state|=t<<u.BLEND_DST_SHIFT}}]),t}(),l=function(){function t(e){i(this,t),this._uniformName=e,this._texture=null}return n(t,[{key:"texture",get:function(){return this._texture},set:function(t){this._texture=t}}]),t}(),h=function(){function t(e,r,n){i(this,t),this._uniformName=e,this._value=r,this._length=n,this._length||(r instanceof Array?this._length=r.length:this._length=1)}return n(t,[{key:"value",get:function(){return this._value},set:function(t){this._value=t}}]),t}();e.Material=function(){function t(){i(this,t),this.state=new f,this.renderOrder=s.DEFAULT,this._samplers=[],this._uniforms=[]}return n(t,[{key:"defineSampler",value:function(t){var e=new l(t);return this._samplers.push(e),e}},{key:"defineUniform",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,n=new h(t,e,r);return this._uniforms.push(n),n}},{key:"getProgramDefines",value:function(t){return{}}},{key:"materialName",get:function(){return null}},{key:"vertexSource",get:function(){return null}},{key:"fragmentSource",get:function(){return null}}]),t}()},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return u}),r.d(e,"identity",function(){return s}),r.d(e,"setAxisAngle",function(){return c}),r.d(e,"getAxisAngle",function(){return f}),r.d(e,"multiply",function(){return l}),r.d(e,"rotateX",function(){return h}),r.d(e,"rotateY",function(){return d}),r.d(e,"rotateZ",function(){return v}),r.d(e,"calculateW",function(){return _}),r.d(e,"slerp",function(){return m}),r.d(e,"random",function(){return p}),r.d(e,"invert",function(){return y}),r.d(e,"conjugate",function(){return b}),r.d(e,"fromMat3",function(){return g}),r.d(e,"fromEuler",function(){return M}),r.d(e,"str",function(){return x}),r.d(e,"clone",function(){return T}),r.d(e,"fromValues",function(){return E}),r.d(e,"copy",function(){return w}),r.d(e,"set",function(){return O}),r.d(e,"add",function(){return R}),r.d(e,"mul",function(){return P}),r.d(e,"scale",function(){return S}),r.d(e,"dot",function(){return A}),r.d(e,"lerp",function(){return N}),r.d(e,"length",function(){return C}),r.d(e,"len",function(){return I}),r.d(e,"squaredLength",function(){return L}),r.d(e,"sqrLen",function(){return k}),r.d(e,"normalize",function(){return F}),r.d(e,"exactEquals",function(){return D}),r.d(e,"equals",function(){return j}),r.d(e,"rotationTo",function(){return B}),r.d(e,"sqlerp",function(){return U}),r.d(e,"setAxes",function(){return V});var n=r(0),i=r(12),o=r(5),a=r(1);function u(){let t=new n.ARRAY_TYPE(4);return n.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function s(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t}function c(t,e,r){r*=.5;let n=Math.sin(r);return t[0]=n*e[0],t[1]=n*e[1],t[2]=n*e[2],t[3]=Math.cos(r),t}function f(t,e){let r=2*Math.acos(e[3]),i=Math.sin(r/2);return i>n.EPSILON?(t[0]=e[0]/i,t[1]=e[1]/i,t[2]=e[2]/i):(t[0]=1,t[1]=0,t[2]=0),r}function l(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=r[0],s=r[1],c=r[2],f=r[3];return t[0]=n*f+a*u+i*c-o*s,t[1]=i*f+a*s+o*u-n*c,t[2]=o*f+a*c+n*s-i*u,t[3]=a*f-n*u-i*s-o*c,t}function h(t,e,r){r*=.5;let n=e[0],i=e[1],o=e[2],a=e[3],u=Math.sin(r),s=Math.cos(r);return t[0]=n*s+a*u,t[1]=i*s+o*u,t[2]=o*s-i*u,t[3]=a*s-n*u,t}function d(t,e,r){r*=.5;let n=e[0],i=e[1],o=e[2],a=e[3],u=Math.sin(r),s=Math.cos(r);return t[0]=n*s-o*u,t[1]=i*s+a*u,t[2]=o*s+n*u,t[3]=a*s-i*u,t}function v(t,e,r){r*=.5;let n=e[0],i=e[1],o=e[2],a=e[3],u=Math.sin(r),s=Math.cos(r);return t[0]=n*s+i*u,t[1]=i*s-n*u,t[2]=o*s+a*u,t[3]=a*s-o*u,t}function _(t,e){let r=e[0],n=e[1],i=e[2];return t[0]=r,t[1]=n,t[2]=i,t[3]=Math.sqrt(Math.abs(1-r*r-n*n-i*i)),t}function m(t,e,r,i){let o,a,u,s,c,f=e[0],l=e[1],h=e[2],d=e[3],v=r[0],_=r[1],m=r[2],p=r[3];return(a=f*v+l*_+h*m+d*p)<0&&(a=-a,v=-v,_=-_,m=-m,p=-p),1-a>n.EPSILON?(o=Math.acos(a),u=Math.sin(o),s=Math.sin((1-i)*o)/u,c=Math.sin(i*o)/u):(s=1-i,c=i),t[0]=s*f+c*v,t[1]=s*l+c*_,t[2]=s*h+c*m,t[3]=s*d+c*p,t}function p(t){let e=n.RANDOM(),r=n.RANDOM(),i=n.RANDOM(),o=Math.sqrt(1-e),a=Math.sqrt(e);return t[0]=o*Math.sin(2*Math.PI*r),t[1]=o*Math.cos(2*Math.PI*r),t[2]=a*Math.sin(2*Math.PI*i),t[3]=a*Math.cos(2*Math.PI*i),t}function y(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=r*r+n*n+i*i+o*o,u=a?1/a:0;return t[0]=-r*u,t[1]=-n*u,t[2]=-i*u,t[3]=o*u,t}function b(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t}function g(t,e){let r,n=e[0]+e[4]+e[8];if(n>0)r=Math.sqrt(n+1),t[3]=.5*r,r=.5/r,t[0]=(e[5]-e[7])*r,t[1]=(e[6]-e[2])*r,t[2]=(e[1]-e[3])*r;else{let n=0;e[4]>e[0]&&(n=1),e[8]>e[3*n+n]&&(n=2);let i=(n+1)%3,o=(n+2)%3;r=Math.sqrt(e[3*n+n]-e[3*i+i]-e[3*o+o]+1),t[n]=.5*r,r=.5/r,t[3]=(e[3*i+o]-e[3*o+i])*r,t[i]=(e[3*i+n]+e[3*n+i])*r,t[o]=(e[3*o+n]+e[3*n+o])*r}return t}function M(t,e,r,n){let i=.5*Math.PI/180;e*=i,r*=i,n*=i;let o=Math.sin(e),a=Math.cos(e),u=Math.sin(r),s=Math.cos(r),c=Math.sin(n),f=Math.cos(n);return t[0]=o*s*f-a*u*c,t[1]=a*u*f+o*s*c,t[2]=a*s*c-o*u*f,t[3]=a*s*f+o*u*c,t}function x(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"}const T=a.clone,E=a.fromValues,w=a.copy,O=a.set,R=a.add,P=l,S=a.scale,A=a.dot,N=a.lerp,C=a.length,I=C,L=a.squaredLength,k=L,F=a.normalize,D=a.exactEquals,j=a.equals,B=function(){let t=o.create(),e=o.fromValues(1,0,0),r=o.fromValues(0,1,0);return function(n,i,a){let u=o.dot(i,a);return u<-.999999?(o.cross(t,e,i),o.len(t)<1e-6&&o.cross(t,r,i),o.normalize(t,t),c(n,t,Math.PI),n):u>.999999?(n[0]=0,n[1]=0,n[2]=0,n[3]=1,n):(o.cross(t,i,a),n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=1+u,F(n,n))}}(),U=function(){let t=u(),e=u();return function(r,n,i,o,a,u){return m(t,n,a,u),m(e,i,o,u),m(r,t,e,2*u*(1-u)),r}}(),V=function(){let t=i.create();return function(e,r,n,i){return t[0]=n[0],t[3]=n[1],t[6]=n[2],t[1]=i[0],t[4]=i[1],t[7]=i[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],F(e,g(e,t))}}()},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"length",function(){return a}),r.d(e,"fromValues",function(){return u}),r.d(e,"copy",function(){return s}),r.d(e,"set",function(){return c}),r.d(e,"add",function(){return f}),r.d(e,"subtract",function(){return l}),r.d(e,"multiply",function(){return h}),r.d(e,"divide",function(){return d}),r.d(e,"ceil",function(){return v}),r.d(e,"floor",function(){return _}),r.d(e,"min",function(){return m}),r.d(e,"max",function(){return p}),r.d(e,"round",function(){return y}),r.d(e,"scale",function(){return b}),r.d(e,"scaleAndAdd",function(){return g}),r.d(e,"distance",function(){return M}),r.d(e,"squaredDistance",function(){return x}),r.d(e,"squaredLength",function(){return T}),r.d(e,"negate",function(){return E}),r.d(e,"inverse",function(){return w}),r.d(e,"normalize",function(){return O}),r.d(e,"dot",function(){return R}),r.d(e,"cross",function(){return P}),r.d(e,"lerp",function(){return S}),r.d(e,"hermite",function(){return A}),r.d(e,"bezier",function(){return N}),r.d(e,"random",function(){return C}),r.d(e,"transformMat4",function(){return I}),r.d(e,"transformMat3",function(){return L}),r.d(e,"transformQuat",function(){return k}),r.d(e,"rotateX",function(){return F}),r.d(e,"rotateY",function(){return D}),r.d(e,"rotateZ",function(){return j}),r.d(e,"angle",function(){return B}),r.d(e,"str",function(){return U}),r.d(e,"exactEquals",function(){return V}),r.d(e,"equals",function(){return Y}),r.d(e,"sub",function(){return G}),r.d(e,"mul",function(){return q}),r.d(e,"div",function(){return H}),r.d(e,"dist",function(){return X}),r.d(e,"sqrDist",function(){return W}),r.d(e,"len",function(){return K}),r.d(e,"sqrLen",function(){return z}),r.d(e,"forEach",function(){return Q});var n=r(0);function i(){let t=new n.ARRAY_TYPE(3);return n.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function o(t){var e=new n.ARRAY_TYPE(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e}function a(t){let e=t[0],r=t[1],n=t[2];return Math.sqrt(e*e+r*r+n*n)}function u(t,e,r){let i=new n.ARRAY_TYPE(3);return i[0]=t,i[1]=e,i[2]=r,i}function s(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t}function c(t,e,r,n){return t[0]=e,t[1]=r,t[2]=n,t}function f(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t}function l(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t}function h(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t}function d(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t}function v(t,e){return t[0]=Math.ceil(e[0]),t[1]=Math.ceil(e[1]),t[2]=Math.ceil(e[2]),t}function _(t,e){return t[0]=Math.floor(e[0]),t[1]=Math.floor(e[1]),t[2]=Math.floor(e[2]),t}function m(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t}function p(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t}function y(t,e){return t[0]=Math.round(e[0]),t[1]=Math.round(e[1]),t[2]=Math.round(e[2]),t}function b(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t}function g(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t}function M(t,e){let r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2];return Math.sqrt(r*r+n*n+i*i)}function x(t,e){let r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2];return r*r+n*n+i*i}function T(t){let e=t[0],r=t[1],n=t[2];return e*e+r*r+n*n}function E(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t}function w(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t}function O(t,e){let r=e[0],n=e[1],i=e[2],o=r*r+n*n+i*i;return o>0&&(o=1/Math.sqrt(o),t[0]=e[0]*o,t[1]=e[1]*o,t[2]=e[2]*o),t}function R(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function P(t,e,r){let n=e[0],i=e[1],o=e[2],a=r[0],u=r[1],s=r[2];return t[0]=i*s-o*u,t[1]=o*a-n*s,t[2]=n*u-i*a,t}function S(t,e,r,n){let i=e[0],o=e[1],a=e[2];return t[0]=i+n*(r[0]-i),t[1]=o+n*(r[1]-o),t[2]=a+n*(r[2]-a),t}function A(t,e,r,n,i,o){let a=o*o,u=a*(2*o-3)+1,s=a*(o-2)+o,c=a*(o-1),f=a*(3-2*o);return t[0]=e[0]*u+r[0]*s+n[0]*c+i[0]*f,t[1]=e[1]*u+r[1]*s+n[1]*c+i[1]*f,t[2]=e[2]*u+r[2]*s+n[2]*c+i[2]*f,t}function N(t,e,r,n,i,o){let a=1-o,u=a*a,s=o*o,c=u*a,f=3*o*u,l=3*s*a,h=s*o;return t[0]=e[0]*c+r[0]*f+n[0]*l+i[0]*h,t[1]=e[1]*c+r[1]*f+n[1]*l+i[1]*h,t[2]=e[2]*c+r[2]*f+n[2]*l+i[2]*h,t}function C(t,e){e=e||1;let r=2*n.RANDOM()*Math.PI,i=2*n.RANDOM()-1,o=Math.sqrt(1-i*i)*e;return t[0]=Math.cos(r)*o,t[1]=Math.sin(r)*o,t[2]=i*e,t}function I(t,e,r){let n=e[0],i=e[1],o=e[2],a=r[3]*n+r[7]*i+r[11]*o+r[15];return a=a||1,t[0]=(r[0]*n+r[4]*i+r[8]*o+r[12])/a,t[1]=(r[1]*n+r[5]*i+r[9]*o+r[13])/a,t[2]=(r[2]*n+r[6]*i+r[10]*o+r[14])/a,t}function L(t,e,r){let n=e[0],i=e[1],o=e[2];return t[0]=n*r[0]+i*r[3]+o*r[6],t[1]=n*r[1]+i*r[4]+o*r[7],t[2]=n*r[2]+i*r[5]+o*r[8],t}function k(t,e,r){let n=r[0],i=r[1],o=r[2],a=r[3],u=e[0],s=e[1],c=e[2],f=i*c-o*s,l=o*u-n*c,h=n*s-i*u,d=i*h-o*l,v=o*f-n*h,_=n*l-i*f,m=2*a;return f*=m,l*=m,h*=m,d*=2,v*=2,_*=2,t[0]=u+f+d,t[1]=s+l+v,t[2]=c+h+_,t}function F(t,e,r,n){let i=[],o=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],o[0]=i[0],o[1]=i[1]*Math.cos(n)-i[2]*Math.sin(n),o[2]=i[1]*Math.sin(n)+i[2]*Math.cos(n),t[0]=o[0]+r[0],t[1]=o[1]+r[1],t[2]=o[2]+r[2],t}function D(t,e,r,n){let i=[],o=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],o[0]=i[2]*Math.sin(n)+i[0]*Math.cos(n),o[1]=i[1],o[2]=i[2]*Math.cos(n)-i[0]*Math.sin(n),t[0]=o[0]+r[0],t[1]=o[1]+r[1],t[2]=o[2]+r[2],t}function j(t,e,r,n){let i=[],o=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],o[0]=i[0]*Math.cos(n)-i[1]*Math.sin(n),o[1]=i[0]*Math.sin(n)+i[1]*Math.cos(n),o[2]=i[2],t[0]=o[0]+r[0],t[1]=o[1]+r[1],t[2]=o[2]+r[2],t}function B(t,e){let r=u(t[0],t[1],t[2]),n=u(e[0],e[1],e[2]);O(r,r),O(n,n);let i=R(r,n);return i>1?0:i<-1?Math.PI:Math.acos(i)}function U(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"}function V(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]}function Y(t,e){let r=t[0],i=t[1],o=t[2],a=e[0],u=e[1],s=e[2];return Math.abs(r-a)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(a))&&Math.abs(i-u)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(u))&&Math.abs(o-s)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(s))}const G=l,q=h,H=d,X=M,W=x,K=a,z=T,Q=function(){let t=i();return function(e,r,n,i,o,a){let u,s;for(r||(r=3),n||(n=0),s=i?Math.min(i*r+n,e.length):e.length,u=n;u<s;u+=r)t[0]=e[u],t[1]=e[u+1],t[2]=e[u+2],o(t,t,a),e[u]=t[0],e[u+1]=t[1],e[u+2]=t[2];return e}}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.vec4=e.vec3=e.vec2=e.quat2=e.quat=e.mat4=e.mat3=e.mat2d=e.mat2=e.glMatrix=void 0;var n=d(r(0)),i=d(r(17)),o=d(r(18)),a=d(r(12)),u=d(r(10)),s=d(r(4)),c=d(r(19)),f=d(r(20)),l=d(r(5)),h=d(r(1));function d(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e}e.glMatrix=n,e.mat2=i,e.mat2d=o,e.mat3=a,e.mat4=u,e.quat=s,e.quat2=c,e.vec2=f,e.vec3=l,e.vec4=h},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Primitive=e.PrimitiveAttribute=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(6);function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}e.PrimitiveAttribute=function t(e,r,n,i,a,u){o(this,t),this.name=e,this.buffer=r,this.componentCount=n||3,this.componentType=i||5126,this.stride=a||0,this.byteOffset=u||0,this.normalized=!1},e.Primitive=function(){function t(e,r,n){o(this,t),this.attributes=e||[],this.elementCount=r||0,this.mode=n||4,this.indexBuffer=null,this.indexByteOffset=0,this.indexType=0,this._min=null,this._max=null}return n(t,[{key:"setIndexBuffer",value:function(t,e,r){this.indexBuffer=t,this.indexByteOffset=e||0,this.indexType=r||5123}},{key:"setBounds",value:function(t,e){this._min=i.vec3.clone(t),this._max=i.vec3.clone(e)}}]),t}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function o(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var u=WebGLRenderingContext,s=e.TextureSampler=function t(){a(this,t),this.minFilter=null,this.magFilter=null,this.wrapS=null,this.wrapT=null},c=e.Texture=function(){function t(){a(this,t),this.sampler=new s,this.mipmap=!0}return n(t,[{key:"format",get:function(){return u.RGBA}},{key:"width",get:function(){return 0}},{key:"height",get:function(){return 0}},{key:"textureKey",get:function(){return null}}]),t}(),f=e.ImageTexture=function(t){function e(t){a(this,e);var r=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r._img=t,r._imgBitmap=null,t.src&&t.complete?t.naturalWidth?r._promise=r._finishImage():r._promise=Promise.reject("Image provided had failed to load."):r._promise=new Promise(function(e,n){t.addEventListener("load",function(){return e(r._finishImage())}),t.addEventListener("error",n)}),r}return o(e,c),n(e,[{key:"_finishImage",value:function(){var t=this;return window.createImageBitmap?window.createImageBitmap(this._img).then(function(e){return t._imgBitmap=e,Promise.resolve(t)}):Promise.resolve(this)}},{key:"waitForComplete",value:function(){return this._promise}},{key:"format",get:function(){return u.RGBA}},{key:"width",get:function(){return this._img.width}},{key:"height",get:function(){return this._img.height}},{key:"textureKey",get:function(){return this._img.src}},{key:"source",get:function(){return this._imgBitmap||this._img}}]),e}(),l=(e.UrlTexture=function(t){function e(t){a(this,e);var r=new Image,n=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,r));return r.src=t,n}return o(e,f),e}(),e.BlobTexture=function(t){function e(t){a(this,e);var r=new Image,n=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,r));return r.src=window.URL.createObjectURL(t),n}return o(e,f),e}(),e.VideoTexture=function(t){function e(t){a(this,e);var r=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r._video=t,t.readyState>=2?r._promise=Promise.resolve(r):t.error?r._promise=Promise.reject(t.error):r._promise=new Promise(function(e,n){t.addEventListener("loadeddata",function(){return e(r)}),t.addEventListener("error",n)}),r}return o(e,c),n(e,[{key:"waitForComplete",value:function(){return this._promise}},{key:"format",get:function(){return u.RGBA}},{key:"width",get:function(){return this._video.videoWidth}},{key:"height",get:function(){return this._video.videoHeight}},{key:"textureKey",get:function(){return this._video.src}},{key:"source",get:function(){return this._video}}]),e}(),0),h=e.DataTexture=function(t){function e(t,r,n){var o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:u.RGBA,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:u.UNSIGNED_BYTE;a(this,e);var c=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return c._data=t,c._width=r,c._height=n,c._format=o,c._type=s,c._key="DATA_"+l,l++,c}return o(e,c),n(e,[{key:"format",get:function(){return this._format}},{key:"width",get:function(){return this._width}},{key:"height",get:function(){return this._height}},{key:"textureKey",get:function(){return this._key}}]),e}();e.ColorTexture=function(t){function e(t,r,n,o){a(this,e);var u=new Uint8Array([255*t,255*r,255*n,255*o]),s=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,u,1,1));return s.mipmap=!1,s._key="COLOR_"+u[0]+"_"+u[1]+"_"+u[2]+"_"+u[3],s}return o(e,h),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.GeometryBuilderBase=e.PrimitiveStream=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(7),o=r(6);function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var u=WebGLRenderingContext,s=o.vec3.create(),c=e.PrimitiveStream=function(){function t(e){a(this,t),this._vertices=[],this._indices=[],this._geometryStarted=!1,this._vertexOffset=0,this._vertexIndex=0,this._highIndex=0,this._flipWinding=!1,this._invertNormals=!1,this._transform=null,this._normalTransform=null,this._min=null,this._max=null}return n(t,[{key:"startGeometry",value:function(){if(this._geometryStarted)throw new Error("Attempted to start a new geometry before the previous one was ended.");this._geometryStarted=!0,this._vertexIndex=0,this._highIndex=0}},{key:"endGeometry",value:function(){if(!this._geometryStarted)throw new Error("Attempted to end a geometry before one was started.");if(this._highIndex>=this._vertexIndex)throw new Error("Geometry contains indices that are out of bounds.\n                       (Contains an index of "+this._highIndex+" when the vertex count is "+this._vertexIndex+")");this._geometryStarted=!1,this._vertexOffset+=this._vertexIndex}},{key:"pushVertex",value:function(t,e,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,u=arguments.length>6&&void 0!==arguments[6]?arguments[6]:0,c=arguments.length>7&&void 0!==arguments[7]?arguments[7]:1;if(!this._geometryStarted)throw new Error("Cannot push vertices before calling startGeometry().");return this._transform&&(s[0]=t,s[1]=e,s[2]=r,o.vec3.transformMat4(s,s,this._transform),t=s[0],e=s[1],r=s[2],s[0]=a,s[1]=u,s[2]=c,o.vec3.transformMat3(s,s,this._normalTransform),a=s[0],u=s[1],c=s[2]),this._invertNormals&&(a*=-1,u*=-1,c*=-1),this._vertices.push(t,e,r,n,i,a,u,c),this._min?(this._min[0]=Math.min(this._min[0],t),this._min[1]=Math.min(this._min[1],e),this._min[2]=Math.min(this._min[2],r),this._max[0]=Math.max(this._max[0],t),this._max[1]=Math.max(this._max[1],e),this._max[2]=Math.max(this._max[2],r)):(this._min=o.vec3.fromValues(t,e,r),this._max=o.vec3.fromValues(t,e,r)),this._vertexIndex++}},{key:"pushTriangle",value:function(t,e,r){if(!this._geometryStarted)throw new Error("Cannot push triangles before calling startGeometry().");this._highIndex=Math.max(this._highIndex,t,e,r),t+=this._vertexOffset,e+=this._vertexOffset,r+=this._vertexOffset,this._flipWinding?this._indices.push(r,e,t):this._indices.push(t,e,r)}},{key:"clear",value:function(){if(this._geometryStarted)throw new Error("Cannot clear before ending the current geometry.");this._vertices=[],this._indices=[],this._vertexOffset=0,this._min=null,this._max=null}},{key:"finishPrimitive",value:function(t){if(!this._vertexOffset)throw new Error("Attempted to call finishPrimitive() before creating any geometry.");var e=t.createRenderBuffer(u.ARRAY_BUFFER,new Float32Array(this._vertices)),r=t.createRenderBuffer(u.ELEMENT_ARRAY_BUFFER,new Uint16Array(this._indices)),n=[new i.PrimitiveAttribute("POSITION",e,3,u.FLOAT,32,0),new i.PrimitiveAttribute("TEXCOORD_0",e,2,u.FLOAT,32,12),new i.PrimitiveAttribute("NORMAL",e,3,u.FLOAT,32,20)],o=new i.Primitive(n,this._indices.length);return o.setIndexBuffer(r),o.setBounds(this._min,this._max),o}},{key:"flipWinding",set:function(t){if(this._geometryStarted)throw new Error("Cannot change flipWinding before ending the current geometry.");this._flipWinding=t},get:function(){this._flipWinding}},{key:"invertNormals",set:function(t){if(this._geometryStarted)throw new Error("Cannot change invertNormals before ending the current geometry.");this._invertNormals=t},get:function(){this._invertNormals}},{key:"transform",set:function(t){if(this._geometryStarted)throw new Error("Cannot change transform before ending the current geometry.");this._transform=t,this._transform&&(this._normalTransform||(this._normalTransform=o.mat3.create()),o.mat3.fromMat4(this._normalTransform,this._transform))},get:function(){this._transform}},{key:"nextVertexIndex",get:function(){return this._vertexIndex}}]),t}();e.GeometryBuilderBase=function(){function t(e){a(this,t),this._stream=e||new c}return n(t,[{key:"finishPrimitive",value:function(t){return this._stream.finishPrimitive(t)}},{key:"clear",value:function(){this._stream.clear()}},{key:"primitiveStream",set:function(t){this._stream=t},get:function(){return this._stream}}]),t}()},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"copy",function(){return a}),r.d(e,"fromValues",function(){return u}),r.d(e,"set",function(){return s}),r.d(e,"identity",function(){return c}),r.d(e,"transpose",function(){return f}),r.d(e,"invert",function(){return l}),r.d(e,"adjoint",function(){return h}),r.d(e,"determinant",function(){return d}),r.d(e,"multiply",function(){return v}),r.d(e,"translate",function(){return _}),r.d(e,"scale",function(){return m}),r.d(e,"rotate",function(){return p}),r.d(e,"rotateX",function(){return y}),r.d(e,"rotateY",function(){return b}),r.d(e,"rotateZ",function(){return g}),r.d(e,"fromTranslation",function(){return M}),r.d(e,"fromScaling",function(){return x}),r.d(e,"fromRotation",function(){return T}),r.d(e,"fromXRotation",function(){return E}),r.d(e,"fromYRotation",function(){return w}),r.d(e,"fromZRotation",function(){return O}),r.d(e,"fromRotationTranslation",function(){return R}),r.d(e,"fromQuat2",function(){return P}),r.d(e,"getTranslation",function(){return S}),r.d(e,"getScaling",function(){return A}),r.d(e,"getRotation",function(){return N}),r.d(e,"fromRotationTranslationScale",function(){return C}),r.d(e,"fromRotationTranslationScaleOrigin",function(){return I}),r.d(e,"fromQuat",function(){return L}),r.d(e,"frustum",function(){return k}),r.d(e,"perspective",function(){return F}),r.d(e,"perspectiveFromFieldOfView",function(){return D}),r.d(e,"ortho",function(){return j}),r.d(e,"lookAt",function(){return B}),r.d(e,"targetTo",function(){return U}),r.d(e,"str",function(){return V}),r.d(e,"frob",function(){return Y}),r.d(e,"add",function(){return G}),r.d(e,"subtract",function(){return q}),r.d(e,"multiplyScalar",function(){return H}),r.d(e,"multiplyScalarAndAdd",function(){return X}),r.d(e,"exactEquals",function(){return W}),r.d(e,"equals",function(){return K}),r.d(e,"mul",function(){return z}),r.d(e,"sub",function(){return Q});var n=r(0);function i(){let t=new n.ARRAY_TYPE(16);return n.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0),t[0]=1,t[5]=1,t[10]=1,t[15]=1,t}function o(t){let e=new n.ARRAY_TYPE(16);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e}function a(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t}function u(t,e,r,i,o,a,u,s,c,f,l,h,d,v,_,m){let p=new n.ARRAY_TYPE(16);return p[0]=t,p[1]=e,p[2]=r,p[3]=i,p[4]=o,p[5]=a,p[6]=u,p[7]=s,p[8]=c,p[9]=f,p[10]=l,p[11]=h,p[12]=d,p[13]=v,p[14]=_,p[15]=m,p}function s(t,e,r,n,i,o,a,u,s,c,f,l,h,d,v,_,m){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t[4]=o,t[5]=a,t[6]=u,t[7]=s,t[8]=c,t[9]=f,t[10]=l,t[11]=h,t[12]=d,t[13]=v,t[14]=_,t[15]=m,t}function c(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function f(t,e){if(t===e){let r=e[1],n=e[2],i=e[3],o=e[6],a=e[7],u=e[11];t[1]=e[4],t[2]=e[8],t[3]=e[12],t[4]=r,t[6]=e[9],t[7]=e[13],t[8]=n,t[9]=o,t[11]=e[14],t[12]=i,t[13]=a,t[14]=u}else t[0]=e[0],t[1]=e[4],t[2]=e[8],t[3]=e[12],t[4]=e[1],t[5]=e[5],t[6]=e[9],t[7]=e[13],t[8]=e[2],t[9]=e[6],t[10]=e[10],t[11]=e[14],t[12]=e[3],t[13]=e[7],t[14]=e[11],t[15]=e[15];return t}function l(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=e[6],c=e[7],f=e[8],l=e[9],h=e[10],d=e[11],v=e[12],_=e[13],m=e[14],p=e[15],y=r*u-n*a,b=r*s-i*a,g=r*c-o*a,M=n*s-i*u,x=n*c-o*u,T=i*c-o*s,E=f*_-l*v,w=f*m-h*v,O=f*p-d*v,R=l*m-h*_,P=l*p-d*_,S=h*p-d*m,A=y*S-b*P+g*R+M*O-x*w+T*E;return A?(A=1/A,t[0]=(u*S-s*P+c*R)*A,t[1]=(i*P-n*S-o*R)*A,t[2]=(_*T-m*x+p*M)*A,t[3]=(h*x-l*T-d*M)*A,t[4]=(s*O-a*S-c*w)*A,t[5]=(r*S-i*O+o*w)*A,t[6]=(m*g-v*T-p*b)*A,t[7]=(f*T-h*g+d*b)*A,t[8]=(a*P-u*O+c*E)*A,t[9]=(n*O-r*P-o*E)*A,t[10]=(v*x-_*g+p*y)*A,t[11]=(l*g-f*x-d*y)*A,t[12]=(u*w-a*R-s*E)*A,t[13]=(r*R-n*w+i*E)*A,t[14]=(_*b-v*M-m*y)*A,t[15]=(f*M-l*b+h*y)*A,t):null}function h(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=e[6],c=e[7],f=e[8],l=e[9],h=e[10],d=e[11],v=e[12],_=e[13],m=e[14],p=e[15];return t[0]=u*(h*p-d*m)-l*(s*p-c*m)+_*(s*d-c*h),t[1]=-(n*(h*p-d*m)-l*(i*p-o*m)+_*(i*d-o*h)),t[2]=n*(s*p-c*m)-u*(i*p-o*m)+_*(i*c-o*s),t[3]=-(n*(s*d-c*h)-u*(i*d-o*h)+l*(i*c-o*s)),t[4]=-(a*(h*p-d*m)-f*(s*p-c*m)+v*(s*d-c*h)),t[5]=r*(h*p-d*m)-f*(i*p-o*m)+v*(i*d-o*h),t[6]=-(r*(s*p-c*m)-a*(i*p-o*m)+v*(i*c-o*s)),t[7]=r*(s*d-c*h)-a*(i*d-o*h)+f*(i*c-o*s),t[8]=a*(l*p-d*_)-f*(u*p-c*_)+v*(u*d-c*l),t[9]=-(r*(l*p-d*_)-f*(n*p-o*_)+v*(n*d-o*l)),t[10]=r*(u*p-c*_)-a*(n*p-o*_)+v*(n*c-o*u),t[11]=-(r*(u*d-c*l)-a*(n*d-o*l)+f*(n*c-o*u)),t[12]=-(a*(l*m-h*_)-f*(u*m-s*_)+v*(u*h-s*l)),t[13]=r*(l*m-h*_)-f*(n*m-i*_)+v*(n*h-i*l),t[14]=-(r*(u*m-s*_)-a*(n*m-i*_)+v*(n*s-i*u)),t[15]=r*(u*h-s*l)-a*(n*h-i*l)+f*(n*s-i*u),t}function d(t){let e=t[0],r=t[1],n=t[2],i=t[3],o=t[4],a=t[5],u=t[6],s=t[7],c=t[8],f=t[9],l=t[10],h=t[11],d=t[12],v=t[13],_=t[14],m=t[15];return(e*a-r*o)*(l*m-h*_)-(e*u-n*o)*(f*m-h*v)+(e*s-i*o)*(f*_-l*v)+(r*u-n*a)*(c*m-h*d)-(r*s-i*a)*(c*_-l*d)+(n*s-i*u)*(c*v-f*d)}function v(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=e[6],f=e[7],l=e[8],h=e[9],d=e[10],v=e[11],_=e[12],m=e[13],p=e[14],y=e[15],b=r[0],g=r[1],M=r[2],x=r[3];return t[0]=b*n+g*u+M*l+x*_,t[1]=b*i+g*s+M*h+x*m,t[2]=b*o+g*c+M*d+x*p,t[3]=b*a+g*f+M*v+x*y,b=r[4],g=r[5],M=r[6],x=r[7],t[4]=b*n+g*u+M*l+x*_,t[5]=b*i+g*s+M*h+x*m,t[6]=b*o+g*c+M*d+x*p,t[7]=b*a+g*f+M*v+x*y,b=r[8],g=r[9],M=r[10],x=r[11],t[8]=b*n+g*u+M*l+x*_,t[9]=b*i+g*s+M*h+x*m,t[10]=b*o+g*c+M*d+x*p,t[11]=b*a+g*f+M*v+x*y,b=r[12],g=r[13],M=r[14],x=r[15],t[12]=b*n+g*u+M*l+x*_,t[13]=b*i+g*s+M*h+x*m,t[14]=b*o+g*c+M*d+x*p,t[15]=b*a+g*f+M*v+x*y,t}function _(t,e,r){let n,i,o,a,u,s,c,f,l,h,d,v,_=r[0],m=r[1],p=r[2];return e===t?(t[12]=e[0]*_+e[4]*m+e[8]*p+e[12],t[13]=e[1]*_+e[5]*m+e[9]*p+e[13],t[14]=e[2]*_+e[6]*m+e[10]*p+e[14],t[15]=e[3]*_+e[7]*m+e[11]*p+e[15]):(n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=e[6],f=e[7],l=e[8],h=e[9],d=e[10],v=e[11],t[0]=n,t[1]=i,t[2]=o,t[3]=a,t[4]=u,t[5]=s,t[6]=c,t[7]=f,t[8]=l,t[9]=h,t[10]=d,t[11]=v,t[12]=n*_+u*m+l*p+e[12],t[13]=i*_+s*m+h*p+e[13],t[14]=o*_+c*m+d*p+e[14],t[15]=a*_+f*m+v*p+e[15]),t}function m(t,e,r){let n=r[0],i=r[1],o=r[2];return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t[3]=e[3]*n,t[4]=e[4]*i,t[5]=e[5]*i,t[6]=e[6]*i,t[7]=e[7]*i,t[8]=e[8]*o,t[9]=e[9]*o,t[10]=e[10]*o,t[11]=e[11]*o,t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t}function p(t,e,r,i){let o,a,u,s,c,f,l,h,d,v,_,m,p,y,b,g,M,x,T,E,w,O,R,P,S=i[0],A=i[1],N=i[2],C=Math.sqrt(S*S+A*A+N*N);return C<n.EPSILON?null:(S*=C=1/C,A*=C,N*=C,o=Math.sin(r),u=1-(a=Math.cos(r)),s=e[0],c=e[1],f=e[2],l=e[3],h=e[4],d=e[5],v=e[6],_=e[7],m=e[8],p=e[9],y=e[10],b=e[11],g=S*S*u+a,M=A*S*u+N*o,x=N*S*u-A*o,T=S*A*u-N*o,E=A*A*u+a,w=N*A*u+S*o,O=S*N*u+A*o,R=A*N*u-S*o,P=N*N*u+a,t[0]=s*g+h*M+m*x,t[1]=c*g+d*M+p*x,t[2]=f*g+v*M+y*x,t[3]=l*g+_*M+b*x,t[4]=s*T+h*E+m*w,t[5]=c*T+d*E+p*w,t[6]=f*T+v*E+y*w,t[7]=l*T+_*E+b*w,t[8]=s*O+h*R+m*P,t[9]=c*O+d*R+p*P,t[10]=f*O+v*R+y*P,t[11]=l*O+_*R+b*P,e!==t&&(t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t)}function y(t,e,r){let n=Math.sin(r),i=Math.cos(r),o=e[4],a=e[5],u=e[6],s=e[7],c=e[8],f=e[9],l=e[10],h=e[11];return e!==t&&(t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[4]=o*i+c*n,t[5]=a*i+f*n,t[6]=u*i+l*n,t[7]=s*i+h*n,t[8]=c*i-o*n,t[9]=f*i-a*n,t[10]=l*i-u*n,t[11]=h*i-s*n,t}function b(t,e,r){let n=Math.sin(r),i=Math.cos(r),o=e[0],a=e[1],u=e[2],s=e[3],c=e[8],f=e[9],l=e[10],h=e[11];return e!==t&&(t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[0]=o*i-c*n,t[1]=a*i-f*n,t[2]=u*i-l*n,t[3]=s*i-h*n,t[8]=o*n+c*i,t[9]=a*n+f*i,t[10]=u*n+l*i,t[11]=s*n+h*i,t}function g(t,e,r){let n=Math.sin(r),i=Math.cos(r),o=e[0],a=e[1],u=e[2],s=e[3],c=e[4],f=e[5],l=e[6],h=e[7];return e!==t&&(t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),t[0]=o*i+c*n,t[1]=a*i+f*n,t[2]=u*i+l*n,t[3]=s*i+h*n,t[4]=c*i-o*n,t[5]=f*i-a*n,t[6]=l*i-u*n,t[7]=h*i-s*n,t}function M(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=e[0],t[13]=e[1],t[14]=e[2],t[15]=1,t}function x(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=e[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=e[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function T(t,e,r){let i,o,a,u=r[0],s=r[1],c=r[2],f=Math.sqrt(u*u+s*s+c*c);return f<n.EPSILON?null:(u*=f=1/f,s*=f,c*=f,i=Math.sin(e),a=1-(o=Math.cos(e)),t[0]=u*u*a+o,t[1]=s*u*a+c*i,t[2]=c*u*a-s*i,t[3]=0,t[4]=u*s*a-c*i,t[5]=s*s*a+o,t[6]=c*s*a+u*i,t[7]=0,t[8]=u*c*a+s*i,t[9]=s*c*a-u*i,t[10]=c*c*a+o,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t)}function E(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=n,t[6]=r,t[7]=0,t[8]=0,t[9]=-r,t[10]=n,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function w(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=0,t[2]=-r,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=r,t[9]=0,t[10]=n,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function O(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=r,t[2]=0,t[3]=0,t[4]=-r,t[5]=n,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function R(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=n+n,s=i+i,c=o+o,f=n*u,l=n*s,h=n*c,d=i*s,v=i*c,_=o*c,m=a*u,p=a*s,y=a*c;return t[0]=1-(d+_),t[1]=l+y,t[2]=h-p,t[3]=0,t[4]=l-y,t[5]=1-(f+_),t[6]=v+m,t[7]=0,t[8]=h+p,t[9]=v-m,t[10]=1-(f+d),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function P(t,e){let r=new n.ARRAY_TYPE(3),i=-e[0],o=-e[1],a=-e[2],u=e[3],s=e[4],c=e[5],f=e[6],l=e[7],h=i*i+o*o+a*a+u*u;return h>0?(r[0]=2*(s*u+l*i+c*a-f*o)/h,r[1]=2*(c*u+l*o+f*i-s*a)/h,r[2]=2*(f*u+l*a+s*o-c*i)/h):(r[0]=2*(s*u+l*i+c*a-f*o),r[1]=2*(c*u+l*o+f*i-s*a),r[2]=2*(f*u+l*a+s*o-c*i)),R(t,e,r),t}function S(t,e){return t[0]=e[12],t[1]=e[13],t[2]=e[14],t}function A(t,e){let r=e[0],n=e[1],i=e[2],o=e[4],a=e[5],u=e[6],s=e[8],c=e[9],f=e[10];return t[0]=Math.sqrt(r*r+n*n+i*i),t[1]=Math.sqrt(o*o+a*a+u*u),t[2]=Math.sqrt(s*s+c*c+f*f),t}function N(t,e){let r=e[0]+e[5]+e[10],n=0;return r>0?(n=2*Math.sqrt(r+1),t[3]=.25*n,t[0]=(e[6]-e[9])/n,t[1]=(e[8]-e[2])/n,t[2]=(e[1]-e[4])/n):e[0]>e[5]&&e[0]>e[10]?(n=2*Math.sqrt(1+e[0]-e[5]-e[10]),t[3]=(e[6]-e[9])/n,t[0]=.25*n,t[1]=(e[1]+e[4])/n,t[2]=(e[8]+e[2])/n):e[5]>e[10]?(n=2*Math.sqrt(1+e[5]-e[0]-e[10]),t[3]=(e[8]-e[2])/n,t[0]=(e[1]+e[4])/n,t[1]=.25*n,t[2]=(e[6]+e[9])/n):(n=2*Math.sqrt(1+e[10]-e[0]-e[5]),t[3]=(e[1]-e[4])/n,t[0]=(e[8]+e[2])/n,t[1]=(e[6]+e[9])/n,t[2]=.25*n),t}function C(t,e,r,n){let i=e[0],o=e[1],a=e[2],u=e[3],s=i+i,c=o+o,f=a+a,l=i*s,h=i*c,d=i*f,v=o*c,_=o*f,m=a*f,p=u*s,y=u*c,b=u*f,g=n[0],M=n[1],x=n[2];return t[0]=(1-(v+m))*g,t[1]=(h+b)*g,t[2]=(d-y)*g,t[3]=0,t[4]=(h-b)*M,t[5]=(1-(l+m))*M,t[6]=(_+p)*M,t[7]=0,t[8]=(d+y)*x,t[9]=(_-p)*x,t[10]=(1-(l+v))*x,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function I(t,e,r,n,i){let o=e[0],a=e[1],u=e[2],s=e[3],c=o+o,f=a+a,l=u+u,h=o*c,d=o*f,v=o*l,_=a*f,m=a*l,p=u*l,y=s*c,b=s*f,g=s*l,M=n[0],x=n[1],T=n[2],E=i[0],w=i[1],O=i[2],R=(1-(_+p))*M,P=(d+g)*M,S=(v-b)*M,A=(d-g)*x,N=(1-(h+p))*x,C=(m+y)*x,I=(v+b)*T,L=(m-y)*T,k=(1-(h+_))*T;return t[0]=R,t[1]=P,t[2]=S,t[3]=0,t[4]=A,t[5]=N,t[6]=C,t[7]=0,t[8]=I,t[9]=L,t[10]=k,t[11]=0,t[12]=r[0]+E-(R*E+A*w+I*O),t[13]=r[1]+w-(P*E+N*w+L*O),t[14]=r[2]+O-(S*E+C*w+k*O),t[15]=1,t}function L(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=r+r,u=n+n,s=i+i,c=r*a,f=n*a,l=n*u,h=i*a,d=i*u,v=i*s,_=o*a,m=o*u,p=o*s;return t[0]=1-l-v,t[1]=f+p,t[2]=h-m,t[3]=0,t[4]=f-p,t[5]=1-c-v,t[6]=d+_,t[7]=0,t[8]=h+m,t[9]=d-_,t[10]=1-c-l,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function k(t,e,r,n,i,o,a){let u=1/(r-e),s=1/(i-n),c=1/(o-a);return t[0]=2*o*u,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*o*s,t[6]=0,t[7]=0,t[8]=(r+e)*u,t[9]=(i+n)*s,t[10]=(a+o)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=a*o*2*c,t[15]=0,t}function F(t,e,r,n,i){let o,a=1/Math.tan(e/2);return t[0]=a/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=i&&i!==1/0?(o=1/(n-i),t[10]=(i+n)*o,t[14]=2*i*n*o):(t[10]=-1,t[14]=-2*n),t}function D(t,e,r,n){let i=Math.tan(e.upDegrees*Math.PI/180),o=Math.tan(e.downDegrees*Math.PI/180),a=Math.tan(e.leftDegrees*Math.PI/180),u=Math.tan(e.rightDegrees*Math.PI/180),s=2/(a+u),c=2/(i+o);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(a-u)*s*.5,t[9]=(i-o)*c*.5,t[10]=n/(r-n),t[11]=-1,t[12]=0,t[13]=0,t[14]=n*r/(r-n),t[15]=0,t}function j(t,e,r,n,i,o,a){let u=1/(e-r),s=1/(n-i),c=1/(o-a);return t[0]=-2*u,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(e+r)*u,t[13]=(i+n)*s,t[14]=(a+o)*c,t[15]=1,t}function B(t,e,r,i){let o,a,u,s,f,l,h,d,v,_,m=e[0],p=e[1],y=e[2],b=i[0],g=i[1],M=i[2],x=r[0],T=r[1],E=r[2];return Math.abs(m-x)<n.EPSILON&&Math.abs(p-T)<n.EPSILON&&Math.abs(y-E)<n.EPSILON?c(t):(h=m-x,d=p-T,v=y-E,o=g*(v*=_=1/Math.sqrt(h*h+d*d+v*v))-M*(d*=_),a=M*(h*=_)-b*v,u=b*d-g*h,(_=Math.sqrt(o*o+a*a+u*u))?(o*=_=1/_,a*=_,u*=_):(o=0,a=0,u=0),s=d*u-v*a,f=v*o-h*u,l=h*a-d*o,(_=Math.sqrt(s*s+f*f+l*l))?(s*=_=1/_,f*=_,l*=_):(s=0,f=0,l=0),t[0]=o,t[1]=s,t[2]=h,t[3]=0,t[4]=a,t[5]=f,t[6]=d,t[7]=0,t[8]=u,t[9]=l,t[10]=v,t[11]=0,t[12]=-(o*m+a*p+u*y),t[13]=-(s*m+f*p+l*y),t[14]=-(h*m+d*p+v*y),t[15]=1,t)}function U(t,e,r,n){let i=e[0],o=e[1],a=e[2],u=n[0],s=n[1],c=n[2],f=i-r[0],l=o-r[1],h=a-r[2],d=f*f+l*l+h*h;d>0&&(f*=d=1/Math.sqrt(d),l*=d,h*=d);let v=s*h-c*l,_=c*f-u*h,m=u*l-s*f;return(d=v*v+_*_+m*m)>0&&(v*=d=1/Math.sqrt(d),_*=d,m*=d),t[0]=v,t[1]=_,t[2]=m,t[3]=0,t[4]=l*m-h*_,t[5]=h*v-f*m,t[6]=f*_-l*v,t[7]=0,t[8]=f,t[9]=l,t[10]=h,t[11]=0,t[12]=i,t[13]=o,t[14]=a,t[15]=1,t}function V(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"}function Y(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))}function G(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t[4]=e[4]+r[4],t[5]=e[5]+r[5],t[6]=e[6]+r[6],t[7]=e[7]+r[7],t[8]=e[8]+r[8],t[9]=e[9]+r[9],t[10]=e[10]+r[10],t[11]=e[11]+r[11],t[12]=e[12]+r[12],t[13]=e[13]+r[13],t[14]=e[14]+r[14],t[15]=e[15]+r[15],t}function q(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t[4]=e[4]-r[4],t[5]=e[5]-r[5],t[6]=e[6]-r[6],t[7]=e[7]-r[7],t[8]=e[8]-r[8],t[9]=e[9]-r[9],t[10]=e[10]-r[10],t[11]=e[11]-r[11],t[12]=e[12]-r[12],t[13]=e[13]-r[13],t[14]=e[14]-r[14],t[15]=e[15]-r[15],t}function H(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*r,t[5]=e[5]*r,t[6]=e[6]*r,t[7]=e[7]*r,t[8]=e[8]*r,t[9]=e[9]*r,t[10]=e[10]*r,t[11]=e[11]*r,t[12]=e[12]*r,t[13]=e[13]*r,t[14]=e[14]*r,t[15]=e[15]*r,t}function X(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t[4]=e[4]+r[4]*n,t[5]=e[5]+r[5]*n,t[6]=e[6]+r[6]*n,t[7]=e[7]+r[7]*n,t[8]=e[8]+r[8]*n,t[9]=e[9]+r[9]*n,t[10]=e[10]+r[10]*n,t[11]=e[11]+r[11]*n,t[12]=e[12]+r[12]*n,t[13]=e[13]+r[13]*n,t[14]=e[14]+r[14]*n,t[15]=e[15]+r[15]*n,t}function W(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]&&t[6]===e[6]&&t[7]===e[7]&&t[8]===e[8]&&t[9]===e[9]&&t[10]===e[10]&&t[11]===e[11]&&t[12]===e[12]&&t[13]===e[13]&&t[14]===e[14]&&t[15]===e[15]}function K(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=t[4],s=t[5],c=t[6],f=t[7],l=t[8],h=t[9],d=t[10],v=t[11],_=t[12],m=t[13],p=t[14],y=t[15],b=e[0],g=e[1],M=e[2],x=e[3],T=e[4],E=e[5],w=e[6],O=e[7],R=e[8],P=e[9],S=e[10],A=e[11],N=e[12],C=e[13],I=e[14],L=e[15];return Math.abs(r-b)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(b))&&Math.abs(i-g)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(g))&&Math.abs(o-M)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(M))&&Math.abs(a-x)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(x))&&Math.abs(u-T)<=n.EPSILON*Math.max(1,Math.abs(u),Math.abs(T))&&Math.abs(s-E)<=n.EPSILON*Math.max(1,Math.abs(s),Math.abs(E))&&Math.abs(c-w)<=n.EPSILON*Math.max(1,Math.abs(c),Math.abs(w))&&Math.abs(f-O)<=n.EPSILON*Math.max(1,Math.abs(f),Math.abs(O))&&Math.abs(l-R)<=n.EPSILON*Math.max(1,Math.abs(l),Math.abs(R))&&Math.abs(h-P)<=n.EPSILON*Math.max(1,Math.abs(h),Math.abs(P))&&Math.abs(d-S)<=n.EPSILON*Math.max(1,Math.abs(d),Math.abs(S))&&Math.abs(v-A)<=n.EPSILON*Math.max(1,Math.abs(v),Math.abs(A))&&Math.abs(_-N)<=n.EPSILON*Math.max(1,Math.abs(_),Math.abs(N))&&Math.abs(m-C)<=n.EPSILON*Math.max(1,Math.abs(m),Math.abs(C))&&Math.abs(p-I)<=n.EPSILON*Math.max(1,Math.abs(p),Math.abs(I))&&Math.abs(y-L)<=n.EPSILON*Math.max(1,Math.abs(y),Math.abs(L))}const z=v,Q=q},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Renderer=e.RenderTexture=e.RenderView=e.ATTRIB_MASK=e.ATTRIB=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();e.createWebGLContext=p;var i=r(3),o=r(2),a=r(21),u=r(8),s=r(6);function c(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var f=e.ATTRIB={POSITION:1,NORMAL:2,TANGENT:3,TEXCOORD_0:4,TEXCOORD_1:5,COLOR_0:6},l=e.ATTRIB_MASK={POSITION:1,NORMAL:2,TANGENT:4,TEXCOORD_0:8,TEXCOORD_1:16,COLOR_0:32},h=WebGLRenderingContext,d=new Float32Array([-.1,-1,-.2]),v=new Float32Array([3,3,3]),_=new RegExp("precision (lowp|mediump|highp) float;");function m(t){return 0==(t&t-1)}function p(t){t=t||{alpha:!1};var e=document.createElement("canvas"),r=t.webgl2?["webgl2"]:["webgl","experimental-webgl"],n=null,i=!0,o=!1,a=void 0;try{for(var u,s=r[Symbol.iterator]();!(i=(u=s.next()).done);i=!0){var c=u.value;if(n=e.getContext(c,t))break}}catch(t){o=!0,a=t}finally{try{!i&&s.return&&s.return()}finally{if(o)throw a}}if(!n){var f=t.webgl2?"WebGL 2":"WebGL";return console.error("This browser does not support "+f+"."),null}return n}e.RenderView=function(){function t(e,r){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"left";c(this,t),this.projectionMatrix=e,this.viewMatrix=r,this.viewport=n,this._eye=i,this._eyeIndex="left"==i?0:1}return n(t,[{key:"eye",get:function(){return this._eye},set:function(t){this._eye=t,this._eyeIndex="left"==t?0:1}},{key:"eyeIndex",get:function(){return this._eyeIndex}}]),t}();var y=function(){function t(e,r,n){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;c(this,t),this._target=e,this._usage=r,this._length=o,n instanceof Promise?(this._buffer=null,this._promise=n.then(function(t){return i._buffer=t,i})):(this._buffer=n,this._promise=Promise.resolve(this))}return n(t,[{key:"waitForComplete",value:function(){return this._promise}}]),t}(),b=function t(e){c(this,t),this._attrib_index=f[e.name],this._componentCount=e.componentCount,this._componentType=e.componentType,this._stride=e.stride,this._byteOffset=e.byteOffset,this._normalized=e.normalized},g=function t(e){c(this,t),this._buffer=e,this._attributes=[]},M=function(){function t(e){c(this,t),this._activeFrameId=0,this._instances=[],this._material=null,this.setPrimitive(e)}return n(t,[{key:"setPrimitive",value:function(t){this._mode=t.mode,this._elementCount=t.elementCount,this._promise=null,this._vao=null,this._complete=!1,this._attributeBuffers=[],this._attributeMask=0;var e=!0,r=!1,n=void 0;try{for(var i,o=t.attributes[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;this._attributeMask|=l[a.name];var u=new b(a),c=!1,f=!0,h=!1,d=void 0;try{for(var v,_=this._attributeBuffers[Symbol.iterator]();!(f=(v=_.next()).done);f=!0){var m=v.value;if(m._buffer==a.buffer){m._attributes.push(u),c=!0;break}}}catch(t){h=!0,d=t}finally{try{!f&&_.return&&_.return()}finally{if(h)throw d}}if(!c){var p=new g(a.buffer);p._attributes.push(u),this._attributeBuffers.push(p)}}}catch(t){r=!0,n=t}finally{try{!e&&o.return&&o.return()}finally{if(r)throw n}}this._indexBuffer=null,this._indexByteOffset=0,this._indexType=0,t.indexBuffer&&(this._indexByteOffset=t.indexByteOffset,this._indexType=t.indexType,this._indexBuffer=t.indexBuffer),t._min?(this._min=s.vec3.clone(t._min),this._max=s.vec3.clone(t._max)):(this._min=null,this._max=null),null!=this._material&&this.waitForComplete()}},{key:"setRenderMaterial",value:function(t){this._material=t,this._promise=null,this._complete=!1,null!=this._material&&this.waitForComplete()}},{key:"markActive",value:function(t){if(this._complete&&this._activeFrameId!=t){if(this._material&&!this._material.markActive(t))return;this._activeFrameId=t}}},{key:"waitForComplete",value:function(){var t=this;if(!this._promise){if(!this._material)return Promise.reject("RenderPrimitive does not have a material");var e=[],r=!0,n=!1,i=void 0;try{for(var o,a=this._attributeBuffers[Symbol.iterator]();!(r=(o=a.next()).done);r=!0){var u=o.value;u._buffer._buffer||e.push(u._buffer._promise)}}catch(t){n=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(n)throw i}}this._indexBuffer&&!this._indexBuffer._buffer&&e.push(this._indexBuffer._promise),this._promise=Promise.all(e).then(function(){return t._complete=!0,t})}return this._promise}},{key:"samplers",get:function(){return this._material._samplerDictionary}},{key:"uniforms",get:function(){return this._material._uniform_dictionary}}]),t}(),x=e.RenderTexture=function(){function t(e){c(this,t),this._texture=e,this._complete=!1,this._activeFrameId=0,this._activeCallback=null}return n(t,[{key:"markActive",value:function(t){this._activeCallback&&this._activeFrameId!=t&&(this._activeFrameId=t,this._activeCallback(this))}}]),t}(),T=s.mat4.create();function E(t,e,r,n,i){var o=(i&r)-(n&r);o&&(o>0?t.enable(e):t.disable(e))}var w=function(){function t(e,r,n){c(this,t),this._renderer=e,this._uniformName=r._uniformName,this._renderTexture=e._getRenderTexture(r._texture),this._index=n}return n(t,[{key:"texture",set:function(t){this._renderTexture=this._renderer._getRenderTexture(t)}}]),t}(),O=function(){function t(e){c(this,t),this._uniformName=e._uniformName,this._uniform=null,this._length=e._length,e._value instanceof Array?this._value=new Float32Array(e._value):this._value=new Float32Array([e._value])}return n(t,[{key:"value",set:function(t){if(1==this._value.length)this._value[0]=t;else for(var e=0;e<this._value.length;++e)this._value[e]=t[e]}}]),t}(),R=function(){function t(e,r,n){c(this,t),this._program=n,this._state=r.state._state,this._activeFrameId=0,this._completeForActiveFrame=!1,this._samplerDictionary={},this._samplers=[];for(var o=0;o<r._samplers.length;++o){var a=new w(e,r._samplers[o],o);this._samplers.push(a),this._samplerDictionary[a._uniformName]=a}this._uniform_dictionary={},this._uniforms=[];var u=!0,s=!1,f=void 0;try{for(var l,h=r._uniforms[Symbol.iterator]();!(u=(l=h.next()).done);u=!0){var d=l.value,v=new O(d);this._uniforms.push(v),this._uniform_dictionary[v._uniformName]=v}}catch(t){s=!0,f=t}finally{try{!u&&h.return&&h.return()}finally{if(s)throw f}}this._firstBind=!0,this._renderOrder=r.renderOrder,this._renderOrder==i.RENDER_ORDER.DEFAULT&&(this._state&i.CAP.BLEND?this._renderOrder=i.RENDER_ORDER.TRANSPARENT:this._renderOrder=i.RENDER_ORDER.OPAQUE)}return n(t,[{key:"bind",value:function(t){if(this._firstBind){for(var e=0;e<this._samplers.length;){var r=this._samplers[e];this._program.uniform[r._uniformName]?++e:this._samplers.splice(e,1)}for(var n=0;n<this._uniforms.length;){var i=this._uniforms[n];i._uniform=this._program.uniform[i._uniformName],i._uniform?++n:this._uniforms.splice(n,1)}this._firstBind=!1}var o=!0,a=!1,u=void 0;try{for(var s,c=this._samplers[Symbol.iterator]();!(o=(s=c.next()).done);o=!0){var f=s.value;t.activeTexture(t.TEXTURE0+f._index),f._renderTexture&&f._renderTexture._complete?t.bindTexture(t.TEXTURE_2D,f._renderTexture._texture):t.bindTexture(t.TEXTURE_2D,null)}}catch(t){a=!0,u=t}finally{try{!o&&c.return&&c.return()}finally{if(a)throw u}}var l=!0,h=!1,d=void 0;try{for(var v,_=this._uniforms[Symbol.iterator]();!(l=(v=_.next()).done);l=!0){var m=v.value;switch(m._length){case 1:t.uniform1fv(m._uniform,m._value);break;case 2:t.uniform2fv(m._uniform,m._value);break;case 3:t.uniform3fv(m._uniform,m._value);break;case 4:t.uniform4fv(m._uniform,m._value)}}}catch(t){h=!0,d=t}finally{try{!l&&_.return&&_.return()}finally{if(h)throw d}}}},{key:"markActive",value:function(t){if(this._activeFrameId!=t){this._activeFrameId=t,this._completeForActiveFrame=!0;for(var e=0;e<this._samplers.length;++e){var r=this._samplers[e];if(r._renderTexture){if(!r._renderTexture._complete){this._completeForActiveFrame=!1;break}r._renderTexture.markActive(t)}}}return this._completeForActiveFrame}},{key:"_capsDiff",value:function(t){return t&i.MAT_STATE.CAPS_RANGE^this._state&i.MAT_STATE.CAPS_RANGE}},{key:"_blendDiff",value:function(t){return this._state&i.CAP.BLEND?t&i.MAT_STATE.BLEND_FUNC_RANGE^this._state&i.MAT_STATE.BLEND_FUNC_RANGE:0}},{key:"_depthFuncDiff",value:function(t){return this._state&i.CAP.DEPTH_TEST?t&i.MAT_STATE.DEPTH_FUNC_RANGE^this._state&i.MAT_STATE.DEPTH_FUNC_RANGE:0}},{key:"cullFace",get:function(){return!!(this._state&i.CAP.CULL_FACE)}},{key:"blend",get:function(){return!!(this._state&i.CAP.BLEND)}},{key:"depthTest",get:function(){return!!(this._state&i.CAP.DEPTH_TEST)}},{key:"stencilTest",get:function(){return!!(this._state&i.CAP.STENCIL_TEST)}},{key:"colorMask",get:function(){return!!(this._state&i.CAP.COLOR_MASK)}},{key:"depthMask",get:function(){return!!(this._state&i.CAP.DEPTH_MASK)}},{key:"stencilMask",get:function(){return!!(this._state&i.CAP.STENCIL_MASK)}},{key:"depthFunc",get:function(){return((this._state&i.MAT_STATE.DEPTH_FUNC_RANGE)>>i.MAT_STATE.DEPTH_FUNC_SHIFT)+h.NEVER}},{key:"blendFuncSrc",get:function(){return(0,i.stateToBlendFunc)(this._state,i.MAT_STATE.BLEND_SRC_RANGE,i.MAT_STATE.BLEND_SRC_SHIFT)}},{key:"blendFuncDst",get:function(){return(0,i.stateToBlendFunc)(this._state,i.MAT_STATE.BLEND_DST_RANGE,i.MAT_STATE.BLEND_DST_SHIFT)}}]),t}();e.Renderer=function(){function t(e){c(this,t),this._gl=e||p(),this._frameId=0,this._programCache={},this._textureCache={},this._renderPrimitives=Array(i.RENDER_ORDER.DEFAULT),this._cameraPositions=[],this._vaoExt=e.getExtension("OES_vertex_array_object");var r=e.getShaderPrecisionFormat(e.FRAGMENT_SHADER,e.HIGH_FLOAT);this._defaultFragPrecision=r.precision>0?"highp":"mediump",this._depthMaskNeedsReset=!1,this._colorMaskNeedsReset=!1,this._globalLightColor=s.vec3.clone(v),this._globalLightDir=s.vec3.clone(d)}return n(t,[{key:"createRenderBuffer",value:function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:h.STATIC_DRAW,n=this._gl,i=n.createBuffer();if(e instanceof Promise){var o=new y(t,r,e.then(function(e){return n.bindBuffer(t,i),n.bufferData(t,e,r),o._length=e.byteLength,i}));return o}return n.bindBuffer(t,i),n.bufferData(t,e,r),new y(t,r,i,e.byteLength)}},{key:"updateRenderBuffer",value:function(t,e){var r=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;if(t._buffer){var i=this._gl;i.bindBuffer(t._target,t._buffer),0==n&&t._length==e.byteLength?i.bufferData(t._target,e,t._usage):i.bufferSubData(t._target,n,e)}else t.waitForComplete().then(function(t){r.updateRenderBuffer(t,e,n)})}},{key:"createRenderPrimitive",value:function(t,e){var r=new M(t),n=this._getMaterialProgram(e,r),i=new R(this,e,n);return r.setRenderMaterial(i),this._renderPrimitives[i._renderOrder]||(this._renderPrimitives[i._renderOrder]=[]),this._renderPrimitives[i._renderOrder].push(r),r}},{key:"createMesh",value:function(t,e){var r=new o.Node;return r.addRenderPrimitive(this.createRenderPrimitive(t,e)),r}},{key:"drawViews",value:function(t,e){if(e){var r=this._gl;if(this._frameId++,e.markActive(this._frameId),1==t.length&&t[0].viewport){var n=t[0].viewport;this._gl.viewport(n.x,n.y,n.width,n.height)}for(var i=0;i<t.length;++i){s.mat4.invert(T,t[i].viewMatrix),this._cameraPositions.length<=i&&this._cameraPositions.push(s.vec3.create());var o=this._cameraPositions[i];s.vec3.set(o,0,0,0),s.vec3.transformMat4(o,o,T)}var a=!0,u=!1,c=void 0;try{for(var f,l=this._renderPrimitives[Symbol.iterator]();!(a=(f=l.next()).done);a=!0){var h=f.value;h&&h.length&&this._drawRenderPrimitiveSet(t,h)}}catch(t){u=!0,c=t}finally{try{!a&&l.return&&l.return()}finally{if(u)throw c}}this._vaoExt&&this._vaoExt.bindVertexArrayOES(null),this._depthMaskNeedsReset&&r.depthMask(!0),this._colorMaskNeedsReset&&r.colorMask(!0,!0,!0,!0)}}},{key:"_drawRenderPrimitiveSet",value:function(t,e){var r=this._gl,n=null,i=null,o=0,a=!0,u=!1,s=void 0;try{for(var c,f=e[Symbol.iterator]();!(a=(c=f.next()).done);a=!0){var l=c.value;if(l._activeFrameId==this._frameId){n!=l._material._program&&((n=l._material._program).use(),n.uniform.LIGHT_DIRECTION&&r.uniform3fv(n.uniform.LIGHT_DIRECTION,this._globalLightDir),n.uniform.LIGHT_COLOR&&r.uniform3fv(n.uniform.LIGHT_COLOR,this._globalLightColor),1==t.length&&(r.uniformMatrix4fv(n.uniform.PROJECTION_MATRIX,!1,t[0].projectionMatrix),r.uniformMatrix4fv(n.uniform.VIEW_MATRIX,!1,t[0].viewMatrix),r.uniform3fv(n.uniform.CAMERA_POSITION,this._cameraPositions[0]),r.uniform1i(n.uniform.EYE_INDEX,t[0].eyeIndex))),i!=l._material&&(this._bindMaterialState(l._material,i),l._material.bind(r,n,i),i=l._material),this._vaoExt?l._vao?this._vaoExt.bindVertexArrayOES(l._vao):(l._vao=this._vaoExt.createVertexArrayOES(),this._vaoExt.bindVertexArrayOES(l._vao),this._bindPrimitive(l)):(this._bindPrimitive(l,o),o=l._attributeMask);for(var h=0;h<t.length;++h){var d=t[h];if(t.length>1){if(d.viewport){var v=d.viewport;r.viewport(v.x,v.y,v.width,v.height)}r.uniformMatrix4fv(n.uniform.PROJECTION_MATRIX,!1,d.projectionMatrix),r.uniformMatrix4fv(n.uniform.VIEW_MATRIX,!1,d.viewMatrix),r.uniform3fv(n.uniform.CAMERA_POSITION,this._cameraPositions[h]),r.uniform1i(n.uniform.EYE_INDEX,d.eyeIndex)}var _=!0,m=!1,p=void 0;try{for(var y,b=l._instances[Symbol.iterator]();!(_=(y=b.next()).done);_=!0){var g=y.value;g._activeFrameId==this._frameId&&(r.uniformMatrix4fv(n.uniform.MODEL_MATRIX,!1,g.worldMatrix),l._indexBuffer?r.drawElements(l._mode,l._elementCount,l._indexType,l._indexByteOffset):r.drawArrays(l._mode,0,l._elementCount))}}catch(t){m=!0,p=t}finally{try{!_&&b.return&&b.return()}finally{if(m)throw p}}}}}}catch(t){u=!0,s=t}finally{try{!a&&f.return&&f.return()}finally{if(u)throw s}}}},{key:"_getRenderTexture",value:function(t){var e=this;if(!t)return null;var r=t.textureKey;if(!r)throw new Error("Texure does not have a valid key");if(r in this._textureCache)return this._textureCache[r];var n=this._gl,i=n.createTexture(),o=new x(i);return this._textureCache[r]=o,t instanceof u.DataTexture?(n.bindTexture(n.TEXTURE_2D,i),n.texImage2D(n.TEXTURE_2D,0,t.format,t.width,t.height,0,t.format,t._type,t._data),this._setSamplerParameters(t),o._complete=!0):t.waitForComplete().then(function(){n.bindTexture(n.TEXTURE_2D,i),n.texImage2D(n.TEXTURE_2D,0,t.format,t.format,n.UNSIGNED_BYTE,t.source),e._setSamplerParameters(t),o._complete=!0,t instanceof u.VideoTexture&&t._video.addEventListener("playing",function(){o._activeCallback=function(){t._video.paused||t._video.waiting||(n.bindTexture(n.TEXTURE_2D,i),n.texImage2D(n.TEXTURE_2D,0,t.format,t.format,n.UNSIGNED_BYTE,t.source))}})}),o}},{key:"_setSamplerParameters",value:function(t){var e=this._gl,r=t.sampler,n=m(t.width)&&m(t.height),i=n&&t.mipmap;i&&e.generateMipmap(e.TEXTURE_2D);var o=r.minFilter||(i?e.LINEAR_MIPMAP_LINEAR:e.LINEAR),a=r.wrapS||(n?e.REPEAT:e.CLAMP_TO_EDGE),u=r.wrapT||(n?e.REPEAT:e.CLAMP_TO_EDGE);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,r.magFilter||e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,o),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,a),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,u)}},{key:"_getProgramKey",value:function(t,e){var r=t+":";for(var n in e)r+=n+"="+e[n]+",";return r}},{key:"_getMaterialProgram",value:function(t,e){var r=this,n=t.materialName,i=t.vertexSource,o=t.fragmentSource;if(null==n)throw new Error("Material does not have a name");if(null==i)throw new Error('Material "'+n+'" does not have a vertex source');if(null==o)throw new Error('Material "'+n+'" does not have a fragment source');var u=t.getProgramDefines(e),s=this._getProgramKey(n,u);if(s in this._programCache)return this._programCache[s];var c=i;c+="\nuniform mat4 PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX;\n\nvoid main() {\n  gl_Position = vertex_main(PROJECTION_MATRIX, VIEW_MATRIX, MODEL_MATRIX);\n}\n";var l=(o.match(_)?"":"precision "+this._defaultFragPrecision+" float;\n")+o;l+="\nvoid main() {\n  gl_FragColor = fragment_main();\n}\n";var h=new a.Program(this._gl,c,l,f,u);return this._programCache[s]=h,h.onNextUse(function(e){for(var n=0;n<t._samplers.length;++n){var i=t._samplers[n],o=e.uniform[i._uniformName];o&&r._gl.uniform1i(o,n)}}),h}},{key:"_bindPrimitive",value:function(t,e){var r=this._gl;if(e!=t._attributeMask)for(var n in f)t._attributeMask&l[n]?r.enableVertexAttribArray(f[n]):r.disableVertexAttribArray(f[n]);var i=!0,o=!1,a=void 0;try{for(var u,s=t._attributeBuffers[Symbol.iterator]();!(i=(u=s.next()).done);i=!0){var c=u.value;r.bindBuffer(r.ARRAY_BUFFER,c._buffer._buffer);var h=!0,d=!1,v=void 0;try{for(var _,m=c._attributes[Symbol.iterator]();!(h=(_=m.next()).done);h=!0){var p=_.value;r.vertexAttribPointer(p._attrib_index,p._componentCount,p._componentType,p._normalized,p._stride,p._byteOffset)}}catch(t){d=!0,v=t}finally{try{!h&&m.return&&m.return()}finally{if(d)throw v}}}}catch(t){o=!0,a=t}finally{try{!i&&s.return&&s.return()}finally{if(o)throw a}}t._indexBuffer?r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,t._indexBuffer._buffer):r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,null)}},{key:"_bindMaterialState",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=this._gl,n=t._state,o=e?e._state:~n;if(n!=o){if(t._capsDiff(o)){E(r,r.CULL_FACE,i.CAP.CULL_FACE,o,n),E(r,r.BLEND,i.CAP.BLEND,o,n),E(r,r.DEPTH_TEST,i.CAP.DEPTH_TEST,o,n),E(r,r.STENCIL_TEST,i.CAP.STENCIL_TEST,o,n);var a=(n&i.CAP.COLOR_MASK)-(o&i.CAP.COLOR_MASK);if(a){var u=a>1;this._colorMaskNeedsReset=!u,r.colorMask(u,u,u,u)}var s=(n&i.CAP.DEPTH_MASK)-(o&i.CAP.DEPTH_MASK);s&&(this._depthMaskNeedsReset=!(s>1),r.depthMask(s>1));var c=(n&i.CAP.STENCIL_MASK)-(o&i.CAP.STENCIL_MASK);c&&r.stencilMask(c>1)}t._blendDiff(o)&&r.blendFunc(t.blendFuncSrc,t.blendFuncDst),t._depthFuncDiff(o)&&r.depthFunc(t.depthFunc)}}},{key:"gl",get:function(){return this._gl}},{key:"globalLightColor",set:function(t){s.vec3.copy(this._globalLightColor,t)},get:function(){return s.vec3.clone(this._globalLightColor)}},{key:"globalLightDir",set:function(t){s.vec3.copy(this._globalLightDir,t)},get:function(){return s.vec3.clone(this._globalLightDir)}}]),t}()},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"fromMat4",function(){return o}),r.d(e,"clone",function(){return a}),r.d(e,"copy",function(){return u}),r.d(e,"fromValues",function(){return s}),r.d(e,"set",function(){return c}),r.d(e,"identity",function(){return f}),r.d(e,"transpose",function(){return l}),r.d(e,"invert",function(){return h}),r.d(e,"adjoint",function(){return d}),r.d(e,"determinant",function(){return v}),r.d(e,"multiply",function(){return _}),r.d(e,"translate",function(){return m}),r.d(e,"rotate",function(){return p}),r.d(e,"scale",function(){return y}),r.d(e,"fromTranslation",function(){return b}),r.d(e,"fromRotation",function(){return g}),r.d(e,"fromScaling",function(){return M}),r.d(e,"fromMat2d",function(){return x}),r.d(e,"fromQuat",function(){return T}),r.d(e,"normalFromMat4",function(){return E}),r.d(e,"projection",function(){return w}),r.d(e,"str",function(){return O}),r.d(e,"frob",function(){return R}),r.d(e,"add",function(){return P}),r.d(e,"subtract",function(){return S}),r.d(e,"multiplyScalar",function(){return A}),r.d(e,"multiplyScalarAndAdd",function(){return N}),r.d(e,"exactEquals",function(){return C}),r.d(e,"equals",function(){return I}),r.d(e,"mul",function(){return L}),r.d(e,"sub",function(){return k});var n=r(0);function i(){let t=new n.ARRAY_TYPE(9);return n.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0),t[0]=1,t[4]=1,t[8]=1,t}function o(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[4],t[4]=e[5],t[5]=e[6],t[6]=e[8],t[7]=e[9],t[8]=e[10],t}function a(t){let e=new n.ARRAY_TYPE(9);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e}function u(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t}function s(t,e,r,i,o,a,u,s,c){let f=new n.ARRAY_TYPE(9);return f[0]=t,f[1]=e,f[2]=r,f[3]=i,f[4]=o,f[5]=a,f[6]=u,f[7]=s,f[8]=c,f}function c(t,e,r,n,i,o,a,u,s,c){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t[4]=o,t[5]=a,t[6]=u,t[7]=s,t[8]=c,t}function f(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function l(t,e){if(t===e){let r=e[1],n=e[2],i=e[5];t[1]=e[3],t[2]=e[6],t[3]=r,t[5]=e[7],t[6]=n,t[7]=i}else t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8];return t}function h(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=e[6],c=e[7],f=e[8],l=f*a-u*c,h=-f*o+u*s,d=c*o-a*s,v=r*l+n*h+i*d;return v?(v=1/v,t[0]=l*v,t[1]=(-f*n+i*c)*v,t[2]=(u*n-i*a)*v,t[3]=h*v,t[4]=(f*r-i*s)*v,t[5]=(-u*r+i*o)*v,t[6]=d*v,t[7]=(-c*r+n*s)*v,t[8]=(a*r-n*o)*v,t):null}function d(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=e[6],c=e[7],f=e[8];return t[0]=a*f-u*c,t[1]=i*c-n*f,t[2]=n*u-i*a,t[3]=u*s-o*f,t[4]=r*f-i*s,t[5]=i*o-r*u,t[6]=o*c-a*s,t[7]=n*s-r*c,t[8]=r*a-n*o,t}function v(t){let e=t[0],r=t[1],n=t[2],i=t[3],o=t[4],a=t[5],u=t[6],s=t[7],c=t[8];return e*(c*o-a*s)+r*(-c*i+a*u)+n*(s*i-o*u)}function _(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=e[6],f=e[7],l=e[8],h=r[0],d=r[1],v=r[2],_=r[3],m=r[4],p=r[5],y=r[6],b=r[7],g=r[8];return t[0]=h*n+d*a+v*c,t[1]=h*i+d*u+v*f,t[2]=h*o+d*s+v*l,t[3]=_*n+m*a+p*c,t[4]=_*i+m*u+p*f,t[5]=_*o+m*s+p*l,t[6]=y*n+b*a+g*c,t[7]=y*i+b*u+g*f,t[8]=y*o+b*s+g*l,t}function m(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=e[6],f=e[7],l=e[8],h=r[0],d=r[1];return t[0]=n,t[1]=i,t[2]=o,t[3]=a,t[4]=u,t[5]=s,t[6]=h*n+d*a+c,t[7]=h*i+d*u+f,t[8]=h*o+d*s+l,t}function p(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=e[6],f=e[7],l=e[8],h=Math.sin(r),d=Math.cos(r);return t[0]=d*n+h*a,t[1]=d*i+h*u,t[2]=d*o+h*s,t[3]=d*a-h*n,t[4]=d*u-h*i,t[5]=d*s-h*o,t[6]=c,t[7]=f,t[8]=l,t}function y(t,e,r){let n=r[0],i=r[1];return t[0]=n*e[0],t[1]=n*e[1],t[2]=n*e[2],t[3]=i*e[3],t[4]=i*e[4],t[5]=i*e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t}function b(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=e[0],t[7]=e[1],t[8]=1,t}function g(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=r,t[2]=0,t[3]=-r,t[4]=n,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function M(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=0,t[4]=e[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function x(t,e){return t[0]=e[0],t[1]=e[1],t[2]=0,t[3]=e[2],t[4]=e[3],t[5]=0,t[6]=e[4],t[7]=e[5],t[8]=1,t}function T(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=r+r,u=n+n,s=i+i,c=r*a,f=n*a,l=n*u,h=i*a,d=i*u,v=i*s,_=o*a,m=o*u,p=o*s;return t[0]=1-l-v,t[3]=f-p,t[6]=h+m,t[1]=f+p,t[4]=1-c-v,t[7]=d-_,t[2]=h-m,t[5]=d+_,t[8]=1-c-l,t}function E(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=e[6],c=e[7],f=e[8],l=e[9],h=e[10],d=e[11],v=e[12],_=e[13],m=e[14],p=e[15],y=r*u-n*a,b=r*s-i*a,g=r*c-o*a,M=n*s-i*u,x=n*c-o*u,T=i*c-o*s,E=f*_-l*v,w=f*m-h*v,O=f*p-d*v,R=l*m-h*_,P=l*p-d*_,S=h*p-d*m,A=y*S-b*P+g*R+M*O-x*w+T*E;return A?(A=1/A,t[0]=(u*S-s*P+c*R)*A,t[1]=(s*O-a*S-c*w)*A,t[2]=(a*P-u*O+c*E)*A,t[3]=(i*P-n*S-o*R)*A,t[4]=(r*S-i*O+o*w)*A,t[5]=(n*O-r*P-o*E)*A,t[6]=(_*T-m*x+p*M)*A,t[7]=(m*g-v*T-p*b)*A,t[8]=(v*x-_*g+p*y)*A,t):null}function w(t,e,r){return t[0]=2/e,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t}function O(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"}function R(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))}function P(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t[4]=e[4]+r[4],t[5]=e[5]+r[5],t[6]=e[6]+r[6],t[7]=e[7]+r[7],t[8]=e[8]+r[8],t}function S(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t[4]=e[4]-r[4],t[5]=e[5]-r[5],t[6]=e[6]-r[6],t[7]=e[7]-r[7],t[8]=e[8]-r[8],t}function A(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*r,t[5]=e[5]*r,t[6]=e[6]*r,t[7]=e[7]*r,t[8]=e[8]*r,t}function N(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t[4]=e[4]+r[4]*n,t[5]=e[5]+r[5]*n,t[6]=e[6]+r[6]*n,t[7]=e[7]+r[7]*n,t[8]=e[8]+r[8]*n,t}function C(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]&&t[6]===e[6]&&t[7]===e[7]&&t[8]===e[8]}function I(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=t[4],s=t[5],c=t[6],f=t[7],l=t[8],h=e[0],d=e[1],v=e[2],_=e[3],m=e[4],p=e[5],y=e[6],b=e[7],g=e[8];return Math.abs(r-h)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(i-d)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(d))&&Math.abs(o-v)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(a-_)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(_))&&Math.abs(u-m)<=n.EPSILON*Math.max(1,Math.abs(u),Math.abs(m))&&Math.abs(s-p)<=n.EPSILON*Math.max(1,Math.abs(s),Math.abs(p))&&Math.abs(c-y)<=n.EPSILON*Math.max(1,Math.abs(c),Math.abs(y))&&Math.abs(f-b)<=n.EPSILON*Math.max(1,Math.abs(f),Math.abs(b))&&Math.abs(l-g)<=n.EPSILON*Math.max(1,Math.abs(l),Math.abs(g))}const L=_,k=S},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.BoxBuilder=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(9);e.BoxBuilder=function(t){function e(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,i.GeometryBuilderBase),n(e,[{key:"pushBox",value:function(t,e){var r=this.primitiveStream,n=.5*(e[0]-t[0]),i=.5*(e[1]-t[1]),o=.5*(e[2]-t[2]),a=t[0]+n,u=t[1]+i,s=t[2]+o;r.startGeometry();var c=r.nextVertexIndex;r.pushTriangle(c,c+1,c+2),r.pushTriangle(c,c+2,c+3),r.pushVertex(-n+a,-i+u,-o+s,0,1,0,-1,0),r.pushVertex(+n+a,-i+u,-o+s,1,1,0,-1,0),r.pushVertex(+n+a,-i+u,+o+s,1,0,0,-1,0),r.pushVertex(-n+a,-i+u,+o+s,0,0,0,-1,0),c=r.nextVertexIndex,r.pushTriangle(c,c+2,c+1),r.pushTriangle(c,c+3,c+2),r.pushVertex(-n+a,+i+u,-o+s,0,0,0,1,0),r.pushVertex(+n+a,+i+u,-o+s,1,0,0,1,0),r.pushVertex(+n+a,+i+u,+o+s,1,1,0,1,0),r.pushVertex(-n+a,+i+u,+o+s,0,1,0,1,0),c=r.nextVertexIndex,r.pushTriangle(c,c+2,c+1),r.pushTriangle(c,c+3,c+2),r.pushVertex(-n+a,-i+u,-o+s,0,1,-1,0,0),r.pushVertex(-n+a,+i+u,-o+s,0,0,-1,0,0),r.pushVertex(-n+a,+i+u,+o+s,1,0,-1,0,0),r.pushVertex(-n+a,-i+u,+o+s,1,1,-1,0,0),c=r.nextVertexIndex,r.pushTriangle(c,c+1,c+2),r.pushTriangle(c,c+2,c+3),r.pushVertex(+n+a,-i+u,-o+s,1,1,1,0,0),r.pushVertex(+n+a,+i+u,-o+s,1,0,1,0,0),r.pushVertex(+n+a,+i+u,+o+s,0,0,1,0,0),r.pushVertex(+n+a,-i+u,+o+s,0,1,1,0,0),c=r.nextVertexIndex,r.pushTriangle(c,c+2,c+1),r.pushTriangle(c,c+3,c+2),r.pushVertex(-n+a,-i+u,-o+s,1,1,0,0,-1),r.pushVertex(+n+a,-i+u,-o+s,0,1,0,0,-1),r.pushVertex(+n+a,+i+u,-o+s,0,0,0,0,-1),r.pushVertex(-n+a,+i+u,-o+s,1,0,0,0,-1),c=r.nextVertexIndex,r.pushTriangle(c,c+1,c+2),r.pushTriangle(c,c+2,c+3),r.pushVertex(-n+a,-i+u,+o+s,0,1,0,0,1),r.pushVertex(+n+a,-i+u,+o+s,1,1,0,0,1),r.pushVertex(+n+a,+i+u,+o+s,1,0,0,0,1),r.pushVertex(-n+a,+i+u,+o+s,0,0,0,0,1),r.endGeometry()}},{key:"pushCube",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[0,0,0],e=.5*(arguments.length>1&&void 0!==arguments[1]?arguments[1]:1);this.pushBox([t[0]-e,t[1]-e,t[2]-e],[t[0]+e,t[1]+e,t[2]+e])}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.PbrMaterial=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(11);e.PbrMaterial=function(t){function e(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e);var t=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.baseColor=t.defineSampler("baseColorTex"),t.metallicRoughness=t.defineSampler("metallicRoughnessTex"),t.normal=t.defineSampler("normalTex"),t.occlusion=t.defineSampler("occlusionTex"),t.emissive=t.defineSampler("emissiveTex"),t.baseColorFactor=t.defineUniform("baseColorFactor",[1,1,1,1]),t.metallicRoughnessFactor=t.defineUniform("metallicRoughnessFactor",[1,1]),t.occlusionStrength=t.defineUniform("occlusionStrength",1),t.emissiveFactor=t.defineUniform("emissiveFactor",[0,0,0]),t}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,i.Material),n(e,[{key:"getProgramDefines",value:function(t){var e={};return t._attributeMask&o.ATTRIB_MASK.COLOR_0&&(e.USE_VERTEX_COLOR=1),t._attributeMask&o.ATTRIB_MASK.TEXCOORD_0&&(this.baseColor.texture&&(e.USE_BASE_COLOR_MAP=1),this.normal.texture&&t._attributeMask&o.ATTRIB_MASK.TANGENT&&(e.USE_NORMAL_MAP=1),this.metallicRoughness.texture&&(e.USE_METAL_ROUGH_MAP=1),this.occlusion.texture&&(e.USE_OCCLUSION=1),this.emissive.texture&&(e.USE_EMISSIVE_TEXTURE=1)),this.metallicRoughness.texture&&t._attributeMask&o.ATTRIB_MASK.TEXCOORD_0||1!=this.metallicRoughnessFactor.value[1]||(e.FULLY_ROUGH=1),e}},{key:"materialName",get:function(){return"PBR"}},{key:"vertexSource",get:function(){return"\nattribute vec3 POSITION, NORMAL;\nattribute vec2 TEXCOORD_0, TEXCOORD_1;\n\nuniform vec3 CAMERA_POSITION;\nuniform vec3 LIGHT_DIRECTION;\n\nvarying vec3 vLight; // Vector from vertex to light.\nvarying vec3 vView; // Vector from vertex to camera.\nvarying vec2 vTex;\n\n#ifdef USE_NORMAL_MAP\nattribute vec4 TANGENT;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_VERTEX_COLOR\nattribute vec4 COLOR_0;\nvarying vec4 vCol;\n#endif\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vec3 n = normalize(vec3(model * vec4(NORMAL, 0.0)));\n#ifdef USE_NORMAL_MAP\n  vec3 t = normalize(vec3(model * vec4(TANGENT.xyz, 0.0)));\n  vec3 b = cross(n, t) * TANGENT.w;\n  vTBN = mat3(t, b, n);\n#else\n  vNorm = n;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  vCol = COLOR_0;\n#endif\n\n  vTex = TEXCOORD_0;\n  vec4 mPos = model * vec4(POSITION, 1.0);\n  vLight = -LIGHT_DIRECTION;\n  vView = CAMERA_POSITION - mPos.xyz;\n  return proj * view * mPos;\n}"}},{key:"fragmentSource",get:function(){return"\n#define M_PI 3.14159265\n\nuniform vec4 baseColorFactor;\n#ifdef USE_BASE_COLOR_MAP\nuniform sampler2D baseColorTex;\n#endif\n\nvarying vec3 vLight;\nvarying vec3 vView;\nvarying vec2 vTex;\n\n#ifdef USE_VERTEX_COLOR\nvarying vec4 vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\nuniform sampler2D normalTex;\nvarying mat3 vTBN;\n#else\nvarying vec3 vNorm;\n#endif\n\n#ifdef USE_METAL_ROUGH_MAP\nuniform sampler2D metallicRoughnessTex;\n#endif\nuniform vec2 metallicRoughnessFactor;\n\n#ifdef USE_OCCLUSION\nuniform sampler2D occlusionTex;\nuniform float occlusionStrength;\n#endif\n\n#ifdef USE_EMISSIVE_TEXTURE\nuniform sampler2D emissiveTex;\n#endif\nuniform vec3 emissiveFactor;\n\nuniform vec3 LIGHT_COLOR;\n\nconst vec3 dielectricSpec = vec3(0.04);\nconst vec3 black = vec3(0.0);\n\n\nvec3 lambertDiffuse(vec3 cDiff) {\n  return cDiff / M_PI;\n}\n\nfloat specD(float a, float nDotH) {\n  float aSqr = a * a;\n  float f = ((nDotH * nDotH) * (aSqr - 1.0) + 1.0);\n  return aSqr / (M_PI * f * f);\n}\n\nfloat specG(float roughness, float nDotL, float nDotV) {\n  float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;\n  float gl = nDotL / (nDotL * (1.0 - k) + k);\n  float gv = nDotV / (nDotV * (1.0 - k) + k);\n  return gl * gv;\n}\n\nvec3 specF(float vDotH, vec3 F0) {\n  float exponent = (-5.55473 * vDotH - 6.98316) * vDotH;\n  float base = 2.0;\n  return F0 + (1.0 - F0) * pow(base, exponent);\n}\n\nvec4 fragment_main() {\n#ifdef USE_BASE_COLOR_MAP\n  vec4 baseColor = texture2D(baseColorTex, vTex) * baseColorFactor;\n#else\n  vec4 baseColor = baseColorFactor;\n#endif\n\n#ifdef USE_VERTEX_COLOR\n  baseColor *= vCol;\n#endif\n\n#ifdef USE_NORMAL_MAP\n  vec3 n = texture2D(normalTex, vTex).rgb;\n  n = normalize(vTBN * (2.0 * n - 1.0));\n#else\n  vec3 n = normalize(vNorm);\n#endif\n\n#ifdef FULLY_ROUGH\n  float metallic = 0.0;\n#else\n  float metallic = metallicRoughnessFactor.x;\n#endif\n\n  float roughness = metallicRoughnessFactor.y;\n\n#ifdef USE_METAL_ROUGH_MAP\n  vec4 metallicRoughness = texture2D(metallicRoughnessTex, vTex);\n  metallic *= metallicRoughness.b;\n  roughness *= metallicRoughness.g;\n#endif\n  \n  vec3 l = normalize(vLight);\n  vec3 v = normalize(vView);\n  vec3 h = normalize(l+v);\n\n  float nDotL = clamp(dot(n, l), 0.001, 1.0);\n  float nDotV = abs(dot(n, v)) + 0.001;\n  float nDotH = max(dot(n, h), 0.0);\n  float vDotH = max(dot(v, h), 0.0);\n\n  // From GLTF Spec\n  vec3 cDiff = mix(baseColor.rgb * (1.0 - dielectricSpec.r), black, metallic); // Diffuse color\n  vec3 F0 = mix(dielectricSpec, baseColor.rgb, metallic); // Specular color\n  float a = roughness * roughness;\n\n#ifdef FULLY_ROUGH\n  vec3 specular = F0 * 0.45;\n#else\n  vec3 F = specF(vDotH, F0);\n  float D = specD(a, nDotH);\n  float G = specG(roughness, nDotL, nDotV);\n  vec3 specular = (D * F * G) / (4.0 * nDotL * nDotV);\n#endif\n  float halfLambert = dot(n, l) * 0.5 + 0.5;\n  halfLambert *= halfLambert;\n\n  vec3 color = (halfLambert * LIGHT_COLOR * lambertDiffuse(cDiff)) + specular;\n\n#ifdef USE_OCCLUSION\n  float occlusion = texture2D(occlusionTex, vTex).r;\n  color = mix(color, color * occlusion, occlusionStrength);\n#endif\n  \n  vec3 emissive = emissiveFactor;\n#ifdef USE_EMISSIVE_TEXTURE\n  emissive *= texture2D(emissiveTex, vTex).rgb;\n#endif\n  color += emissive;\n\n  // gamma correction\n  //color = pow(color, vec3(1.0/2.2));\n\n  return vec4(color, baseColor.a);\n}"}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=r(2);Object.defineProperty(e,"Node",{enumerable:!0,get:function(){return n.Node}});var i=r(11);Object.defineProperty(e,"Renderer",{enumerable:!0,get:function(){return i.Renderer}}),Object.defineProperty(e,"createWebGLContext",{enumerable:!0,get:function(){return i.createWebGLContext}});var o=r(8);Object.defineProperty(e,"UrlTexture",{enumerable:!0,get:function(){return o.UrlTexture}});var a=r(9);Object.defineProperty(e,"PrimitiveStream",{enumerable:!0,get:function(){return a.PrimitiveStream}});var u=r(13);Object.defineProperty(e,"BoxBuilder",{enumerable:!0,get:function(){return u.BoxBuilder}});var s=r(14);Object.defineProperty(e,"PbrMaterial",{enumerable:!0,get:function(){return s.PbrMaterial}});var c=r(6);Object.defineProperty(e,"mat4",{enumerable:!0,get:function(){return c.mat4}}),Object.defineProperty(e,"mat3",{enumerable:!0,get:function(){return c.mat3}}),Object.defineProperty(e,"vec3",{enumerable:!0,get:function(){return c.vec3}}),Object.defineProperty(e,"vec4",{enumerable:!0,get:function(){return c.vec4}}),Object.defineProperty(e,"quat",{enumerable:!0,get:function(){return c.quat}});var f=r(22);Object.defineProperty(e,"BoundsRenderer",{enumerable:!0,get:function(){return f.BoundsRenderer}});var l=r(23);Object.defineProperty(e,"ButtonNode",{enumerable:!0,get:function(){return l.ButtonNode}});var h=r(24);Object.defineProperty(e,"DropShadowNode",{enumerable:!0,get:function(){return h.DropShadowNode}});var d=r(25);Object.defineProperty(e,"CubeSeaNode",{enumerable:!0,get:function(){return d.CubeSeaNode}});var v=r(26);Object.defineProperty(e,"Gltf2Node",{enumerable:!0,get:function(){return v.Gltf2Node}});var _=r(28);Object.defineProperty(e,"SkyboxNode",{enumerable:!0,get:function(){return _.SkyboxNode}});var m=r(29);Object.defineProperty(e,"VideoNode",{enumerable:!0,get:function(){return m.VideoNode}});var p=r(30);Object.defineProperty(e,"WebXRView",{enumerable:!0,get:function(){return p.WebXRView}}),Object.defineProperty(e,"Scene",{enumerable:!0,get:function(){return p.Scene}});var y=r(34);Object.defineProperty(e,"FallbackHelper",{enumerable:!0,get:function(){return y.FallbackHelper}});var b=r(35);Object.defineProperty(e,"QueryArgs",{enumerable:!0,get:function(){return b.QueryArgs}})},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Ray=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(6);var o=i.mat3.create();e.Ray=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.origin=i.vec3.create(),this._dir=i.vec3.create(),this._dir[2]=-1,e&&(i.vec3.transformMat4(this.origin,this.origin,e),i.mat3.fromMat4(o,e),i.vec3.transformMat3(this._dir,this._dir,o)),this.dir=this._dir}return n(t,[{key:"intersectsAABB",value:function(t,e){var r=this,n=[t,e],o=(n[r.sign[0]][0]-r.origin[0])*r.inv_dir[0],a=(n[1-r.sign[0]][0]-r.origin[0])*r.inv_dir[0],u=(n[r.sign[1]][1]-r.origin[1])*r.inv_dir[1],s=(n[1-r.sign[1]][1]-r.origin[1])*r.inv_dir[1];if(o>s||u>a)return null;u>o&&(o=u),s<a&&(a=s);var c=(n[r.sign[2]][2]-r.origin[2])*r.inv_dir[2],f=(n[1-r.sign[2]][2]-r.origin[2])*r.inv_dir[2];if(o>f||c>a)return null;c>o&&(o=c),f<a&&(a=f);var l=-1;if(o>0&&a>0)l=Math.min(o,a);else if(o>0)l=o;else{if(!(a>0))return null;l=a}l-=.02;var h=i.vec3.clone(this._dir);return i.vec3.scale(h,h,l),i.vec3.add(h,h,this.origin),h}},{key:"dir",get:function(){return this._dir},set:function(t){this._dir=i.vec3.copy(this._dir,t),i.vec3.normalize(this._dir,this._dir),this.inv_dir=i.vec3.fromValues(1/this._dir[0],1/this._dir[1],1/this._dir[2]),this.sign=[this.inv_dir[0]<0?1:0,this.inv_dir[1]<0?1:0,this.inv_dir[2]<0?1:0]}}]),t}()},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"copy",function(){return a}),r.d(e,"identity",function(){return u}),r.d(e,"fromValues",function(){return s}),r.d(e,"set",function(){return c}),r.d(e,"transpose",function(){return f}),r.d(e,"invert",function(){return l}),r.d(e,"adjoint",function(){return h}),r.d(e,"determinant",function(){return d}),r.d(e,"multiply",function(){return v}),r.d(e,"rotate",function(){return _}),r.d(e,"scale",function(){return m}),r.d(e,"fromRotation",function(){return p}),r.d(e,"fromScaling",function(){return y}),r.d(e,"str",function(){return b}),r.d(e,"frob",function(){return g}),r.d(e,"LDU",function(){return M}),r.d(e,"add",function(){return x}),r.d(e,"subtract",function(){return T}),r.d(e,"exactEquals",function(){return E}),r.d(e,"equals",function(){return w}),r.d(e,"multiplyScalar",function(){return O}),r.d(e,"multiplyScalarAndAdd",function(){return R}),r.d(e,"mul",function(){return P}),r.d(e,"sub",function(){return S});var n=r(0);function i(){let t=new n.ARRAY_TYPE(4);return n.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0),t[0]=1,t[3]=1,t}function o(t){let e=new n.ARRAY_TYPE(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e}function a(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function u(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t}function s(t,e,r,i){let o=new n.ARRAY_TYPE(4);return o[0]=t,o[1]=e,o[2]=r,o[3]=i,o}function c(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}function f(t,e){if(t===e){let r=e[1];t[1]=e[2],t[2]=r}else t[0]=e[0],t[1]=e[2],t[2]=e[1],t[3]=e[3];return t}function l(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=r*o-i*n;return a?(a=1/a,t[0]=o*a,t[1]=-n*a,t[2]=-i*a,t[3]=r*a,t):null}function h(t,e){let r=e[0];return t[0]=e[3],t[1]=-e[1],t[2]=-e[2],t[3]=r,t}function d(t){return t[0]*t[3]-t[2]*t[1]}function v(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=r[0],s=r[1],c=r[2],f=r[3];return t[0]=n*u+o*s,t[1]=i*u+a*s,t[2]=n*c+o*f,t[3]=i*c+a*f,t}function _(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=Math.sin(r),s=Math.cos(r);return t[0]=n*s+o*u,t[1]=i*s+a*u,t[2]=n*-u+o*s,t[3]=i*-u+a*s,t}function m(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=r[0],s=r[1];return t[0]=n*u,t[1]=i*u,t[2]=o*s,t[3]=a*s,t}function p(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=r,t[2]=-r,t[3]=n,t}function y(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=e[1],t}function b(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"}function g(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))}function M(t,e,r,n){return t[2]=n[2]/n[0],r[0]=n[0],r[1]=n[1],r[3]=n[3]-t[2]*r[1],[t,e,r]}function x(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t}function T(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t}function E(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]}function w(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=e[0],s=e[1],c=e[2],f=e[3];return Math.abs(r-u)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(i-s)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(s))&&Math.abs(o-c)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(c))&&Math.abs(a-f)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(f))}function O(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t}function R(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t}const P=v,S=T},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"copy",function(){return a}),r.d(e,"identity",function(){return u}),r.d(e,"fromValues",function(){return s}),r.d(e,"set",function(){return c}),r.d(e,"invert",function(){return f}),r.d(e,"determinant",function(){return l}),r.d(e,"multiply",function(){return h}),r.d(e,"rotate",function(){return d}),r.d(e,"scale",function(){return v}),r.d(e,"translate",function(){return _}),r.d(e,"fromRotation",function(){return m}),r.d(e,"fromScaling",function(){return p}),r.d(e,"fromTranslation",function(){return y}),r.d(e,"str",function(){return b}),r.d(e,"frob",function(){return g}),r.d(e,"add",function(){return M}),r.d(e,"subtract",function(){return x}),r.d(e,"multiplyScalar",function(){return T}),r.d(e,"multiplyScalarAndAdd",function(){return E}),r.d(e,"exactEquals",function(){return w}),r.d(e,"equals",function(){return O}),r.d(e,"mul",function(){return R}),r.d(e,"sub",function(){return P});var n=r(0);function i(){let t=new n.ARRAY_TYPE(6);return n.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0),t[0]=1,t[3]=1,t}function o(t){let e=new n.ARRAY_TYPE(6);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e}function a(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t}function u(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t}function s(t,e,r,i,o,a){let u=new n.ARRAY_TYPE(6);return u[0]=t,u[1]=e,u[2]=r,u[3]=i,u[4]=o,u[5]=a,u}function c(t,e,r,n,i,o,a){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t[4]=o,t[5]=a,t}function f(t,e){let r=e[0],n=e[1],i=e[2],o=e[3],a=e[4],u=e[5],s=r*o-n*i;return s?(s=1/s,t[0]=o*s,t[1]=-n*s,t[2]=-i*s,t[3]=r*s,t[4]=(i*u-o*a)*s,t[5]=(n*a-r*u)*s,t):null}function l(t){return t[0]*t[3]-t[1]*t[2]}function h(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=r[0],f=r[1],l=r[2],h=r[3],d=r[4],v=r[5];return t[0]=n*c+o*f,t[1]=i*c+a*f,t[2]=n*l+o*h,t[3]=i*l+a*h,t[4]=n*d+o*v+u,t[5]=i*d+a*v+s,t}function d(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=Math.sin(r),f=Math.cos(r);return t[0]=n*f+o*c,t[1]=i*f+a*c,t[2]=n*-c+o*f,t[3]=i*-c+a*f,t[4]=u,t[5]=s,t}function v(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=r[0],f=r[1];return t[0]=n*c,t[1]=i*c,t[2]=o*f,t[3]=a*f,t[4]=u,t[5]=s,t}function _(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=e[4],s=e[5],c=r[0],f=r[1];return t[0]=n,t[1]=i,t[2]=o,t[3]=a,t[4]=n*c+o*f+u,t[5]=i*c+a*f+s,t}function m(t,e){let r=Math.sin(e),n=Math.cos(e);return t[0]=n,t[1]=r,t[2]=-r,t[3]=n,t[4]=0,t[5]=0,t}function p(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=e[1],t[4]=0,t[5]=0,t}function y(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=e[0],t[5]=e[1],t}function b(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"}function g(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)}function M(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t[4]=e[4]+r[4],t[5]=e[5]+r[5],t}function x(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t[4]=e[4]-r[4],t[5]=e[5]-r[5],t}function T(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*r,t[5]=e[5]*r,t}function E(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t[4]=e[4]+r[4]*n,t[5]=e[5]+r[5]*n,t}function w(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]}function O(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=t[4],s=t[5],c=e[0],f=e[1],l=e[2],h=e[3],d=e[4],v=e[5];return Math.abs(r-c)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(c))&&Math.abs(i-f)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(f))&&Math.abs(o-l)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(l))&&Math.abs(a-h)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(h))&&Math.abs(u-d)<=n.EPSILON*Math.max(1,Math.abs(u),Math.abs(d))&&Math.abs(s-v)<=n.EPSILON*Math.max(1,Math.abs(s),Math.abs(v))}const R=h,P=x},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return a}),r.d(e,"clone",function(){return u}),r.d(e,"fromValues",function(){return s}),r.d(e,"fromRotationTranslationValues",function(){return c}),r.d(e,"fromRotationTranslation",function(){return f}),r.d(e,"fromTranslation",function(){return l}),r.d(e,"fromRotation",function(){return h}),r.d(e,"fromMat4",function(){return d}),r.d(e,"copy",function(){return v}),r.d(e,"identity",function(){return _}),r.d(e,"set",function(){return m}),r.d(e,"getReal",function(){return p}),r.d(e,"getDual",function(){return y}),r.d(e,"setReal",function(){return b}),r.d(e,"setDual",function(){return g}),r.d(e,"getTranslation",function(){return M}),r.d(e,"translate",function(){return x}),r.d(e,"rotateX",function(){return T}),r.d(e,"rotateY",function(){return E}),r.d(e,"rotateZ",function(){return w}),r.d(e,"rotateByQuatAppend",function(){return O}),r.d(e,"rotateByQuatPrepend",function(){return R}),r.d(e,"rotateAroundAxis",function(){return P}),r.d(e,"add",function(){return S}),r.d(e,"multiply",function(){return A}),r.d(e,"mul",function(){return N}),r.d(e,"scale",function(){return C}),r.d(e,"dot",function(){return I}),r.d(e,"lerp",function(){return L}),r.d(e,"invert",function(){return k}),r.d(e,"conjugate",function(){return F}),r.d(e,"length",function(){return D}),r.d(e,"len",function(){return j}),r.d(e,"squaredLength",function(){return B}),r.d(e,"sqrLen",function(){return U}),r.d(e,"normalize",function(){return V}),r.d(e,"str",function(){return Y}),r.d(e,"exactEquals",function(){return G}),r.d(e,"equals",function(){return q});var n=r(0),i=r(4),o=r(10);function a(){let t=new n.ARRAY_TYPE(8);return n.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[4]=0,t[5]=0,t[6]=0,t[7]=0),t[3]=1,t}function u(t){let e=new n.ARRAY_TYPE(8);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e}function s(t,e,r,i,o,a,u,s){let c=new n.ARRAY_TYPE(8);return c[0]=t,c[1]=e,c[2]=r,c[3]=i,c[4]=o,c[5]=a,c[6]=u,c[7]=s,c}function c(t,e,r,i,o,a,u){let s=new n.ARRAY_TYPE(8);s[0]=t,s[1]=e,s[2]=r,s[3]=i;let c=.5*o,f=.5*a,l=.5*u;return s[4]=c*i+f*r-l*e,s[5]=f*i+l*t-c*r,s[6]=l*i+c*e-f*t,s[7]=-c*t-f*e-l*r,s}function f(t,e,r){let n=.5*r[0],i=.5*r[1],o=.5*r[2],a=e[0],u=e[1],s=e[2],c=e[3];return t[0]=a,t[1]=u,t[2]=s,t[3]=c,t[4]=n*c+i*s-o*u,t[5]=i*c+o*a-n*s,t[6]=o*c+n*u-i*a,t[7]=-n*a-i*u-o*s,t}function l(t,e){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=.5*e[0],t[5]=.5*e[1],t[6]=.5*e[2],t[7]=0,t}function h(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=0,t[5]=0,t[6]=0,t[7]=0,t}function d(t,e){let r=i.create();o.getRotation(r,e);let a=new n.ARRAY_TYPE(3);return o.getTranslation(a,e),f(t,r,a),t}function v(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t}function _(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t[6]=0,t[7]=0,t}function m(t,e,r,n,i,o,a,u,s){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t[4]=o,t[5]=a,t[6]=u,t[7]=s,t}const p=i.copy;function y(t,e){return t[0]=e[4],t[1]=e[5],t[2]=e[6],t[3]=e[7],t}const b=i.copy;function g(t,e){return t[4]=e[0],t[5]=e[1],t[6]=e[2],t[7]=e[3],t}function M(t,e){let r=e[4],n=e[5],i=e[6],o=e[7],a=-e[0],u=-e[1],s=-e[2],c=e[3];return t[0]=2*(r*c+o*a+n*s-i*u),t[1]=2*(n*c+o*u+i*a-r*s),t[2]=2*(i*c+o*s+r*u-n*a),t}function x(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=.5*r[0],s=.5*r[1],c=.5*r[2],f=e[4],l=e[5],h=e[6],d=e[7];return t[0]=n,t[1]=i,t[2]=o,t[3]=a,t[4]=a*u+i*c-o*s+f,t[5]=a*s+o*u-n*c+l,t[6]=a*c+n*s-i*u+h,t[7]=-n*u-i*s-o*c+d,t}function T(t,e,r){let n=-e[0],o=-e[1],a=-e[2],u=e[3],s=e[4],c=e[5],f=e[6],l=e[7],h=s*u+l*n+c*a-f*o,d=c*u+l*o+f*n-s*a,v=f*u+l*a+s*o-c*n,_=l*u-s*n-c*o-f*a;return i.rotateX(t,e,r),n=t[0],o=t[1],a=t[2],u=t[3],t[4]=h*u+_*n+d*a-v*o,t[5]=d*u+_*o+v*n-h*a,t[6]=v*u+_*a+h*o-d*n,t[7]=_*u-h*n-d*o-v*a,t}function E(t,e,r){let n=-e[0],o=-e[1],a=-e[2],u=e[3],s=e[4],c=e[5],f=e[6],l=e[7],h=s*u+l*n+c*a-f*o,d=c*u+l*o+f*n-s*a,v=f*u+l*a+s*o-c*n,_=l*u-s*n-c*o-f*a;return i.rotateY(t,e,r),n=t[0],o=t[1],a=t[2],u=t[3],t[4]=h*u+_*n+d*a-v*o,t[5]=d*u+_*o+v*n-h*a,t[6]=v*u+_*a+h*o-d*n,t[7]=_*u-h*n-d*o-v*a,t}function w(t,e,r){let n=-e[0],o=-e[1],a=-e[2],u=e[3],s=e[4],c=e[5],f=e[6],l=e[7],h=s*u+l*n+c*a-f*o,d=c*u+l*o+f*n-s*a,v=f*u+l*a+s*o-c*n,_=l*u-s*n-c*o-f*a;return i.rotateZ(t,e,r),n=t[0],o=t[1],a=t[2],u=t[3],t[4]=h*u+_*n+d*a-v*o,t[5]=d*u+_*o+v*n-h*a,t[6]=v*u+_*a+h*o-d*n,t[7]=_*u-h*n-d*o-v*a,t}function O(t,e,r){let n=r[0],i=r[1],o=r[2],a=r[3],u=e[0],s=e[1],c=e[2],f=e[3];return t[0]=u*a+f*n+s*o-c*i,t[1]=s*a+f*i+c*n-u*o,t[2]=c*a+f*o+u*i-s*n,t[3]=f*a-u*n-s*i-c*o,u=e[4],s=e[5],c=e[6],f=e[7],t[4]=u*a+f*n+s*o-c*i,t[5]=s*a+f*i+c*n-u*o,t[6]=c*a+f*o+u*i-s*n,t[7]=f*a-u*n-s*i-c*o,t}function R(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=r[0],s=r[1],c=r[2],f=r[3];return t[0]=n*f+a*u+i*c-o*s,t[1]=i*f+a*s+o*u-n*c,t[2]=o*f+a*c+n*s-i*u,t[3]=a*f-n*u-i*s-o*c,u=r[4],s=r[5],c=r[6],f=r[7],t[4]=n*f+a*u+i*c-o*s,t[5]=i*f+a*s+o*u-n*c,t[6]=o*f+a*c+n*s-i*u,t[7]=a*f-n*u-i*s-o*c,t}function P(t,e,r,i){if(Math.abs(i)<n.EPSILON)return v(t,e);let o=Math.sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);i*=.5;let a=Math.sin(i),u=a*r[0]/o,s=a*r[1]/o,c=a*r[2]/o,f=Math.cos(i),l=e[0],h=e[1],d=e[2],_=e[3];t[0]=l*f+_*u+h*c-d*s,t[1]=h*f+_*s+d*u-l*c,t[2]=d*f+_*c+l*s-h*u,t[3]=_*f-l*u-h*s-d*c;let m=e[4],p=e[5],y=e[6],b=e[7];return t[4]=m*f+b*u+p*c-y*s,t[5]=p*f+b*s+y*u-m*c,t[6]=y*f+b*c+m*s-p*u,t[7]=b*f-m*u-p*s-y*c,t}function S(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t[4]=e[4]+r[4],t[5]=e[5]+r[5],t[6]=e[6]+r[6],t[7]=e[7]+r[7],t}function A(t,e,r){let n=e[0],i=e[1],o=e[2],a=e[3],u=r[4],s=r[5],c=r[6],f=r[7],l=e[4],h=e[5],d=e[6],v=e[7],_=r[0],m=r[1],p=r[2],y=r[3];return t[0]=n*y+a*_+i*p-o*m,t[1]=i*y+a*m+o*_-n*p,t[2]=o*y+a*p+n*m-i*_,t[3]=a*y-n*_-i*m-o*p,t[4]=n*f+a*u+i*c-o*s+l*y+v*_+h*p-d*m,t[5]=i*f+a*s+o*u-n*c+h*y+v*m+d*_-l*p,t[6]=o*f+a*c+n*s-i*u+d*y+v*p+l*m-h*_,t[7]=a*f-n*u-i*s-o*c+v*y-l*_-h*m-d*p,t}const N=A;function C(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*r,t[5]=e[5]*r,t[6]=e[6]*r,t[7]=e[7]*r,t}const I=i.dot;function L(t,e,r,n){let i=1-n;return I(e,r)<0&&(n=-n),t[0]=e[0]*i+r[0]*n,t[1]=e[1]*i+r[1]*n,t[2]=e[2]*i+r[2]*n,t[3]=e[3]*i+r[3]*n,t[4]=e[4]*i+r[4]*n,t[5]=e[5]*i+r[5]*n,t[6]=e[6]*i+r[6]*n,t[7]=e[7]*i+r[7]*n,t}function k(t,e){let r=B(e);return t[0]=-e[0]/r,t[1]=-e[1]/r,t[2]=-e[2]/r,t[3]=e[3]/r,t[4]=-e[4]/r,t[5]=-e[5]/r,t[6]=-e[6]/r,t[7]=e[7]/r,t}function F(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t[4]=-e[4],t[5]=-e[5],t[6]=-e[6],t[7]=e[7],t}const D=i.length,j=D,B=i.squaredLength,U=B;function V(t,e){let r=B(e);if(r>0){r=Math.sqrt(r);let n=e[0]/r,i=e[1]/r,o=e[2]/r,a=e[3]/r,u=e[4],s=e[5],c=e[6],f=e[7],l=n*u+i*s+o*c+a*f;t[0]=n,t[1]=i,t[2]=o,t[3]=a,t[4]=(u-n*l)/r,t[5]=(s-i*l)/r,t[6]=(c-o*l)/r,t[7]=(f-a*l)/r}return t}function Y(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"}function G(t,e){return t[0]===e[0]&&t[1]===e[1]&&t[2]===e[2]&&t[3]===e[3]&&t[4]===e[4]&&t[5]===e[5]&&t[6]===e[6]&&t[7]===e[7]}function q(t,e){let r=t[0],i=t[1],o=t[2],a=t[3],u=t[4],s=t[5],c=t[6],f=t[7],l=e[0],h=e[1],d=e[2],v=e[3],_=e[4],m=e[5],p=e[6],y=e[7];return Math.abs(r-l)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(l))&&Math.abs(i-h)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(h))&&Math.abs(o-d)<=n.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(a-v)<=n.EPSILON*Math.max(1,Math.abs(a),Math.abs(v))&&Math.abs(u-_)<=n.EPSILON*Math.max(1,Math.abs(u),Math.abs(_))&&Math.abs(s-m)<=n.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=n.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-y)<=n.EPSILON*Math.max(1,Math.abs(f),Math.abs(y))}},function(t,e,r){"use strict";r.r(e),r.d(e,"create",function(){return i}),r.d(e,"clone",function(){return o}),r.d(e,"fromValues",function(){return a}),r.d(e,"copy",function(){return u}),r.d(e,"set",function(){return s}),r.d(e,"add",function(){return c}),r.d(e,"subtract",function(){return f}),r.d(e,"multiply",function(){return l}),r.d(e,"divide",function(){return h}),r.d(e,"ceil",function(){return d}),r.d(e,"floor",function(){return v}),r.d(e,"min",function(){return _}),r.d(e,"max",function(){return m}),r.d(e,"round",function(){return p}),r.d(e,"scale",function(){return y}),r.d(e,"scaleAndAdd",function(){return b}),r.d(e,"distance",function(){return g}),r.d(e,"squaredDistance",function(){return M}),r.d(e,"length",function(){return x}),r.d(e,"squaredLength",function(){return T}),r.d(e,"negate",function(){return E}),r.d(e,"inverse",function(){return w}),r.d(e,"normalize",function(){return O}),r.d(e,"dot",function(){return R}),r.d(e,"cross",function(){return P}),r.d(e,"lerp",function(){return S}),r.d(e,"random",function(){return A}),r.d(e,"transformMat2",function(){return N}),r.d(e,"transformMat2d",function(){return C}),r.d(e,"transformMat3",function(){return I}),r.d(e,"transformMat4",function(){return L}),r.d(e,"rotate",function(){return k}),r.d(e,"angle",function(){return F}),r.d(e,"str",function(){return D}),r.d(e,"exactEquals",function(){return j}),r.d(e,"equals",function(){return B}),r.d(e,"len",function(){return U}),r.d(e,"sub",function(){return V}),r.d(e,"mul",function(){return Y}),r.d(e,"div",function(){return G}),r.d(e,"dist",function(){return q}),r.d(e,"sqrDist",function(){return H}),r.d(e,"sqrLen",function(){return X}),r.d(e,"forEach",function(){return W});var n=r(0);function i(){let t=new n.ARRAY_TYPE(2);return n.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0),t}function o(t){let e=new n.ARRAY_TYPE(2);return e[0]=t[0],e[1]=t[1],e}function a(t,e){let r=new n.ARRAY_TYPE(2);return r[0]=t,r[1]=e,r}function u(t,e){return t[0]=e[0],t[1]=e[1],t}function s(t,e,r){return t[0]=e,t[1]=r,t}function c(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t}function f(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t}function l(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t}function h(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t}function d(t,e){return t[0]=Math.ceil(e[0]),t[1]=Math.ceil(e[1]),t}function v(t,e){return t[0]=Math.floor(e[0]),t[1]=Math.floor(e[1]),t}function _(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t}function m(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t}function p(t,e){return t[0]=Math.round(e[0]),t[1]=Math.round(e[1]),t}function y(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t}function b(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t}function g(t,e){var r=e[0]-t[0],n=e[1]-t[1];return Math.sqrt(r*r+n*n)}function M(t,e){var r=e[0]-t[0],n=e[1]-t[1];return r*r+n*n}function x(t){var e=t[0],r=t[1];return Math.sqrt(e*e+r*r)}function T(t){var e=t[0],r=t[1];return e*e+r*r}function E(t,e){return t[0]=-e[0],t[1]=-e[1],t}function w(t,e){return t[0]=1/e[0],t[1]=1/e[1],t}function O(t,e){var r=e[0],n=e[1],i=r*r+n*n;return i>0&&(i=1/Math.sqrt(i),t[0]=e[0]*i,t[1]=e[1]*i),t}function R(t,e){return t[0]*e[0]+t[1]*e[1]}function P(t,e,r){var n=e[0]*r[1]-e[1]*r[0];return t[0]=t[1]=0,t[2]=n,t}function S(t,e,r,n){var i=e[0],o=e[1];return t[0]=i+n*(r[0]-i),t[1]=o+n*(r[1]-o),t}function A(t,e){e=e||1;var r=2*n.RANDOM()*Math.PI;return t[0]=Math.cos(r)*e,t[1]=Math.sin(r)*e,t}function N(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i,t[1]=r[1]*n+r[3]*i,t}function C(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i+r[4],t[1]=r[1]*n+r[3]*i+r[5],t}function I(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[3]*i+r[6],t[1]=r[1]*n+r[4]*i+r[7],t}function L(t,e,r){let n=e[0],i=e[1];return t[0]=r[0]*n+r[4]*i+r[12],t[1]=r[1]*n+r[5]*i+r[13],t}function k(t,e,r,n){let i=e[0]-r[0],o=e[1]-r[1],a=Math.sin(n),u=Math.cos(n);return t[0]=i*u-o*a+r[0],t[1]=i*a+o*u+r[1],t}function F(t,e){let r=t[0],n=t[1],i=e[0],o=e[1],a=r*r+n*n;a>0&&(a=1/Math.sqrt(a));let u=i*i+o*o;u>0&&(u=1/Math.sqrt(u));let s=(r*i+n*o)*a*u;return s>1?0:s<-1?Math.PI:Math.acos(s)}function D(t){return"vec2("+t[0]+", "+t[1]+")"}function j(t,e){return t[0]===e[0]&&t[1]===e[1]}function B(t,e){let r=t[0],i=t[1],o=e[0],a=e[1];return Math.abs(r-o)<=n.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(i-a)<=n.EPSILON*Math.max(1,Math.abs(i),Math.abs(a))}const U=x,V=f,Y=l,G=h,q=g,H=M,X=T,W=function(){let t=i();return function(e,r,n,i,o,a){let u,s;for(r||(r=2),n||(n=0),s=i?Math.min(i*r+n,e.length):e.length,u=n;u<s;u+=r)t[0]=e[u],t[1]=e[u+1],o(t,t,a),e[u]=t[0],e[u+1]=t[1];return e}}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();e.Program=function(){function t(e,r,n,i,o){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this._gl=e,this.program=e.createProgram(),this.attrib=null,this.uniform=null,this.defines={},this._firstUse=!0,this._nextUseCallbacks=[];var a="";if(o)for(var u in o)this.defines[u]=o[u],a+="#define "+u+" "+o[u]+"\n";if(this._vertShader=e.createShader(e.VERTEX_SHADER),e.attachShader(this.program,this._vertShader),e.shaderSource(this._vertShader,a+r),e.compileShader(this._vertShader),this._fragShader=e.createShader(e.FRAGMENT_SHADER),e.attachShader(this.program,this._fragShader),e.shaderSource(this._fragShader,a+n),e.compileShader(this._fragShader),i)for(var s in this.attrib={},i)e.bindAttribLocation(this.program,i[s],s),this.attrib[s]=i[s];e.linkProgram(this.program)}return n(t,[{key:"onNextUse",value:function(t){this._nextUseCallbacks.push(t)}},{key:"use",value:function(){var t=this._gl;if(this._firstUse){if(this._firstUse=!1,t.getProgramParameter(this.program,t.LINK_STATUS)){if(!this.attrib){this.attrib={};for(var e=t.getProgramParameter(this.program,t.ACTIVE_ATTRIBUTES),r=0;r<e;r++){var n=t.getActiveAttrib(this.program,r);this.attrib[n.name]=t.getAttribLocation(this.program,n.name)}}this.uniform={};for(var i=t.getProgramParameter(this.program,t.ACTIVE_UNIFORMS),o="",a=0;a<i;a++){o=t.getActiveUniform(this.program,a).name.replace("[0]",""),this.uniform[o]=t.getUniformLocation(this.program,o)}}else t.getShaderParameter(this._vertShader,t.COMPILE_STATUS)?t.getShaderParameter(this._fragShader,t.COMPILE_STATUS)?console.error("Program link error: "+t.getProgramInfoLog(this.program)):console.error("Fragment shader compile error: "+t.getShaderInfoLog(this._fragShader)):console.error("Vertex shader compile error: "+t.getShaderInfoLog(this._vertShader)),t.deleteProgram(this.program),this.program=null;t.deleteShader(this._vertShader),t.deleteShader(this._fragShader)}if(t.useProgram(this.program),this._nextUseCallbacks.length){var u=!0,s=!1,c=void 0;try{for(var f,l=this._nextUseCallbacks[Symbol.iterator]();!(u=(f=l.next()).done);u=!0){(0,f.value)(this)}}catch(t){s=!0,c=t}finally{try{!u&&l.return&&l.return()}finally{if(s)throw c}}this._nextUseCallbacks=[]}}}]),t}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.BoundsRenderer=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(7);function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function c(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var f=WebGLRenderingContext,l=function(t){function e(){u(this,e);var t=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.state.blend=!0,t.state.blendFuncSrc=f.SRC_ALPHA,t.state.blendFuncDst=f.ONE,t.state.depthTest=!1,t}return c(e,i.Material),n(e,[{key:"materialName",get:function(){return"BOUNDS_RENDERER"}},{key:"vertexSource",get:function(){return"\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return"\n    precision mediump float;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 1.0, 0.0, 0.3);\n    }"}}]),e}();e.BoundsRenderer=function(t){function e(){u(this,e);var t=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t._stageBounds=null,t}return c(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){this.stageBounds=this._stageBounds}},{key:"stageBounds",get:function(){return this._stageBounds},set:function(t){if(this._stageBounds&&this.clearRenderPrimitives(),this._stageBounds=t,t&&0!==t.length&&this._renderer){for(var e=[],r=[],n=t.geometry.length,i=0;i<n;i++){var o=t.geometry[i];e.push(o.x,0,o.z),r.push(i,0===i?n-1:i-1,n)}e.push(0,0,0);var u=this._renderer.createRenderBuffer(f.ARRAY_BUFFER,new Float32Array(e)),s=this._renderer.createRenderBuffer(f.ELEMENT_ARRAY_BUFFER,new Uint16Array(r)),c=[new a.PrimitiveAttribute("POSITION",u,3,f.FLOAT,12,0)],h=new a.Primitive(c,r.length);h.setIndexBuffer(s);var d=this._renderer.createRenderPrimitive(h,new l);this.addRenderPrimitive(d)}}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.ButtonNode=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(9);function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function c(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var f=function(t){function e(){u(this,e);var t=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.state.blend=!0,t.defineUniform("hoverAmount",0),t}return c(e,i.Material),n(e,[{key:"materialName",get:function(){return"BUTTON_MATERIAL"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n\n    uniform float hoverAmount;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      float scale = mix(1.0, 1.1, hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }"}},{key:"fragmentSource",get:function(){return"\n    uniform float hoverAmount;\n\n    const vec4 default_color = vec4(0.75, 0.75, 0.75, 0.85);\n    const vec4 hover_color = vec4(0.9, 0.9,\n                                  0.9, 1);\n\n    vec4 fragment_main() {\n      return mix(default_color, hover_color, hoverAmount);\n    }"}}]),e}(),l=function(t){function e(){u(this,e);var t=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.state.blend=!0,t.defineUniform("hoverAmount",0),t.icon=t.defineSampler("icon"),t}return c(e,i.Material),n(e,[{key:"materialName",get:function(){return"BUTTON_ICON_MATERIAL"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    uniform float hoverAmount;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      float scale = mix(1.0, 1.1, hoverAmount);\n      vec4 pos = vec4(POSITION.x * scale, POSITION.y * scale, POSITION.z * (scale + (hoverAmount * 0.2)), 1.0);\n      return proj * view * model * pos;\n    }"}},{key:"fragmentSource",get:function(){return"\n    uniform sampler2D icon;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(icon, vTexCoord);\n    }"}}]),e}();e.ButtonNode=function(t){function e(t,r){u(this,e);var n=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return n.selectable=!0,n._selectHandler=r,n._iconTexture=t,n._hovered=!1,n._hoverT=0,n}return c(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){var e=new a.PrimitiveStream,r=.0025,n=.05,i=n-.025;e.startGeometry();for(var o=0;o<32;++o){var u=o*(2*Math.PI/32),s=.025*Math.cos(u),c=.025*Math.sin(u);switch(Math.floor(o/8)){case 0:s+=i,c+=i;break;case 1:s-=i,c+=i;break;case 2:s-=i,c-=i;break;case 3:s+=i,c-=i}e.pushVertex(s,c,-r,0,0,0,0,1),o>1&&e.pushTriangle(0,o-1,o)}e.endGeometry();var h=e.finishPrimitive(t);this._buttonRenderPrimitive=t.createRenderPrimitive(h,new f),this.addRenderPrimitive(this._buttonRenderPrimitive),n=.035,e.clear(),e.startGeometry(),e.pushVertex(-n,n,r,0,0,0,0,1),e.pushVertex(-n,-n,r,0,1,0,0,1),e.pushVertex(n,-n,r,1,1,0,0,1),e.pushVertex(n,n,r,1,0,0,0,1),e.pushTriangle(0,1,2),e.pushTriangle(0,2,3),e.endGeometry();var d=e.finishPrimitive(t),v=new l;v.icon.texture=this._iconTexture,this._iconRenderPrimitive=t.createRenderPrimitive(d,v),this.addRenderPrimitive(this._iconRenderPrimitive)}},{key:"onHoverStart",value:function(){this._hovered=!0}},{key:"onHoverEnd",value:function(){this._hovered=!1}},{key:"_updateHoverState",value:function(){var t=this._hoverT/200,e=t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1;this._buttonRenderPrimitive.uniforms.hoverAmount.value=e,this._iconRenderPrimitive.uniforms.hoverAmount.value=e}},{key:"onUpdate",value:function(t,e){this._hovered&&this._hoverT<200?(this._hoverT=Math.min(200,this._hoverT+e),this._updateHoverState()):!this._hovered&&this._hoverT>0&&(this._hoverT=Math.max(0,this._hoverT-e),this._updateHoverState())}},{key:"iconTexture",get:function(){return this._iconTexture},set:function(t){this._iconTexture!=t&&(this._iconTexture=t,this._iconRenderPrimitive.samplers.icon.texture=t)}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.DropShadowNode=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(9);function u(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function c(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var f=WebGLRenderingContext,l=function(t){function e(){u(this,e);var t=s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.state.blend=!0,t.state.blendFuncSrc=f.ONE,t.state.blendFuncDst=f.ONE_MINUS_SRC_ALPHA,t.state.depthFunc=f.LEQUAL,t.state.depthMask=!1,t}return c(e,i.Material),n(e,[{key:"materialName",get:function(){return"DROP_SHADOW_MATERIAL"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying float vShadow;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vShadow = TEXCOORD_0.x;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return"\n    varying float vShadow;\n\n    vec4 fragment_main() {\n      return vec4(0.0, 0.0, 0.0, vShadow);\n    }"}}]),e}();e.DropShadowNode=function(t){function e(t,r){return u(this,e),s(this,(e.__proto__||Object.getPrototypeOf(e)).call(this))}return c(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){var e=new a.PrimitiveStream;e.startGeometry(),e.pushVertex(0,.01,0,.7);for(var r=2*Math.PI/32,n=void 0,i=0;i<32;++i){n=e.nextVertexIndex;var o=i*r,u=Math.cos(o),s=Math.sin(o);e.pushVertex(.6*u,.01,.6*s,.3),e.pushVertex(1*u,.01,1*s,0),i>0&&(e.pushTriangle(0,n,n-2),e.pushTriangle(n,n+1,n-1),e.pushTriangle(n,n-1,n-2))}e.pushTriangle(0,1,n),e.pushTriangle(1,2,n+1),e.pushTriangle(1,n+1,n),e.endGeometry();var c=e.finishPrimitive(t);this._shadowRenderPrimitive=t.createRenderPrimitive(c,new l),this.addRenderPrimitive(this._shadowRenderPrimitive)}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.CubeSeaNode=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(8),u=r(13),s=r(6);function c(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function f(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var h=function(t){function e(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];c(this,e);var r=f(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r.heavy=t,r.baseColor=r.defineSampler("baseColor"),r}return l(e,i.Material),n(e,[{key:"materialName",get:function(){return"CUBE_SEA"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    attribute vec3 NORMAL;\n\n    varying vec2 vTexCoord;\n    varying vec3 vLight;\n\n    const vec3 lightDir = vec3(0.75, 0.5, 1.0);\n    const vec3 ambientColor = vec3(0.5, 0.5, 0.5);\n    const vec3 lightColor = vec3(0.75, 0.75, 0.75);\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec3 normalRotated = vec3(model * vec4(NORMAL, 0.0));\n      float lightFactor = max(dot(normalize(lightDir), normalRotated), 0.0);\n      vLight = ambientColor + (lightColor * lightFactor);\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return this.heavy?"\n      precision mediump float;\n\n      uniform sampler2D diffuse;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec2 dimensions = vec2(64, 64);\n      float seed = 0.42;\n\n      vec2 hash( vec2 p ) {\n        p=vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3)));\n        return fract(sin(p)*18.5453);\n      }\n\n      vec3 hash3( vec2 p ) {\n          vec3 q = vec3( dot(p,vec2(127.1,311.7)),\n                 dot(p,vec2(269.5,183.3)),\n                 dot(p,vec2(419.2,371.9)) );\n        return fract(sin(q)*43758.5453);\n      }\n\n      float iqnoise( in vec2 x, float u, float v ) {\n        vec2 p = floor(x);\n        vec2 f = fract(x);\n        float k = 1.0+63.0*pow(1.0-v,4.0);\n        float va = 0.0;\n        float wt = 0.0;\n        for( int j=-2; j<=2; j++ )\n          for( int i=-2; i<=2; i++ ) {\n            vec2 g = vec2( float(i),float(j) );\n            vec3 o = hash3( p + g )*vec3(u,u,1.0);\n            vec2 r = g - f + o.xy;\n            float d = dot(r,r);\n            float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n            va += o.z*ww;\n            wt += ww;\n          }\n        return va/wt;\n      }\n\n      // return distance, and cell id\n      vec2 voronoi( in vec2 x ) {\n        vec2 n = floor( x );\n        vec2 f = fract( x );\n        vec3 m = vec3( 8.0 );\n        for( int j=-1; j<=1; j++ )\n          for( int i=-1; i<=1; i++ ) {\n            vec2  g = vec2( float(i), float(j) );\n            vec2  o = hash( n + g );\n            vec2  r = g - f + (0.5+0.5*sin(seed+6.2831*o));\n            float d = dot( r, r );\n            if( d<m.x )\n              m = vec3( d, o );\n          }\n        return vec2( sqrt(m.x), m.y+m.z );\n      }\n\n      vec4 fragment_main() {\n        vec2 uv = ( vTexCoord );\n        uv *= vec2( 10., 10. );\n        uv += seed;\n        vec2 p = 0.5 - 0.5*sin( 0.*vec2(1.01,1.71) );\n\n        vec2 c = voronoi( uv );\n        vec3 col = vec3( c.y / 2. );\n\n        float f = iqnoise( 1. * uv + c.y, p.x, p.y );\n        col *= 1.0 + .25 * vec3( f );\n\n        return vec4(vLight, 1.0) * texture2D(diffuse, vTexCoord) * vec4( col, 1. );\n      }":"\n      precision mediump float;\n      uniform sampler2D baseColor;\n      varying vec2 vTexCoord;\n      varying vec3 vLight;\n\n      vec4 fragment_main() {\n        return vec4(vLight, 1.0) * texture2D(baseColor, vTexCoord);\n      }"}}]),e}();e.CubeSeaNode=function(t){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};c(this,e);var r=f(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r.heavyGpu=!!t.heavyGpu,r.cubeCount=t.cubeCount||(r.heavyGpu?12:10),r.cubeScale=t.cubeScale||1,r.halfOnly=!!t.halfOnly,r.autoRotate=!!t.autoRotate,r._texture=new a.UrlTexture(t.imageUrl||"media/textures/cube-sea.png"),r._material=new h(r.heavyGpu),r._material.baseColor.texture=r._texture,r._renderPrimitive=null,r}return l(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){this._renderPrimitive=null;var e=new u.BoxBuilder;e.pushCube([0,.25,-.8],.1),e.pushCube([.8,.25,0],.1),e.pushCube([0,.25,.8],.1),e.pushCube([-.8,.25,0],.1);var r=e.finishPrimitive(t);return this.heroNode=t.createMesh(r,this._material),this.rebuildCubes(e),this.cubeSeaNode=new o.Node,this.cubeSeaNode.addRenderPrimitive(this._renderPrimitive),this.addNode(this.cubeSeaNode),this.addNode(this.heroNode),this.waitForComplete()}},{key:"rebuildCubes",value:function(t){if(this._renderer){t?t.clear():t=new u.BoxBuilder;for(var e=.4*this.cubeScale,r=.5*this.cubeCount,n=0;n<this.cubeCount;++n)for(var i=0;i<this.cubeCount;++i)for(var o=0;o<this.cubeCount;++o){var a=[n-r,i-r,o-r];this.halfOnly&&a[0]<0||(0==a[0]&&0==a[1]&&0==a[2]||t.pushCube(a,e))}this.cubeCount>12&&(t.indexType=5125);var s=t.finishPrimitive(this._renderer);this._renderPrimitive?this._renderPrimitive.setPrimitive(s):this._renderPrimitive=this._renderer.createRenderPrimitive(s,this._material)}}},{key:"onUpdate",value:function(t,e){this.autoRotate&&s.mat4.fromRotation(this.cubeSeaNode.matrix,t/500,[0,-1,0]),s.mat4.fromRotation(this.heroNode.matrix,t/2e3,[0,1,0])}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Gltf2Node=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(2),o=r(27);var a=new WeakMap;e.Gltf2Node=function(t){function e(t){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e);var r=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r._url=t.url,r._promise=null,r._resolver=null,r._rejecter=null,r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,i.Node),n(e,[{key:"onRendererChanged",value:function(t){var e=this,r=a.get(t);r||(r=new o.Gltf2Loader(t),a.set(t,r)),!this._resolver&&this._promise&&(this._promise=null),this._ensurePromise(),r.loadFromUrl(this._url).then(function(t){e.addNode(t),e._resolver(t.waitForComplete()),e._resolver=null,e._rejecter=null}).catch(function(t){e._rejecter(t),e._resolver=null,e._rejecter=null})}},{key:"_ensurePromise",value:function(){var t=this;return this._promise||(this._promise=new Promise(function(e,r){t._resolver=e,t._rejecter=r})),this._promise}},{key:"waitForComplete",value:function(){return this._ensurePromise()}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Gltf2Loader=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(14),o=r(2),a=r(7),u=r(8);function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var c=WebGLRenderingContext,f=1313821514,l=5130562;function h(t){return!!t.match(/^data:/)}function d(t,e){return function(t){var e=new RegExp("^"+window.location.protocol,"i");return!!t.match(e)}(t)||h(t)?t:e+t}function v(t){switch(t){case"SCALAR":return 1;case"VEC2":return 2;case"VEC3":return 3;case"VEC4":return 4;default:return 0}}e.Gltf2Loader=function(){function t(e){s(this,t),this.renderer=e,this._gl=e._gl}return n(t,[{key:"loadFromUrl",value:function(t){var e=this;return fetch(t).then(function(r){var n=t.lastIndexOf("/"),i=0!==n?t.substring(0,n+1):"";if(t.endsWith(".gltf"))return r.json().then(function(t){return e.loadFromJson(t,i)});if(t.endsWith(".glb"))return r.arrayBuffer().then(function(t){return e.loadFromBinary(t,i)});throw new Error("Unrecognized file extension")})}},{key:"loadFromBinary",value:function(t,e){var r=new DataView(t,0,12),n=r.getUint32(0,!0),i=r.getUint32(4,!0),o=r.getUint32(8,!0);if(1179937895!=n)throw new Error("Invalid magic string in binary header.");if(2!=i)throw new Error("Incompatible version in binary header.");for(var a={},u=12;u<o;){var s=new DataView(t,u,8),c=s.getUint32(0,!0);a[s.getUint32(4,!0)]=t.slice(u+8,u+8+c),u+=c+8}if(!a[f])throw new Error("File contained no json chunk.");var h=new TextDecoder("utf-8").decode(a[f]),d=JSON.parse(h);return this.loadFromJson(d,e,a[l])}},{key:"loadFromJson",value:function(t,e,r){if(!t.asset)throw new Error("Missing asset description.");if("2.0"!=t.asset.minVersion&&"2.0"!=t.asset.version)throw new Error("Incompatible asset version.");var n=[];if(r)n[0]=new p({},e,r);else{var s=!0,f=!1,l=void 0;try{for(var h,d=t.buffers[Symbol.iterator]();!(s=(h=d.next()).done);s=!0){var y=h.value;n.push(new p(y,e))}}catch(t){f=!0,l=t}finally{try{!s&&d.return&&d.return()}finally{if(f)throw l}}}var b=[],g=!0,M=!1,x=void 0;try{for(var T,E=t.bufferViews[Symbol.iterator]();!(g=(T=E.next()).done);g=!0){var w=T.value;b.push(new m(w,n))}}catch(t){M=!0,x=t}finally{try{!g&&E.return&&E.return()}finally{if(M)throw x}}var O=[];if(t.images){var R=!0,P=!1,S=void 0;try{for(var A,N=t.images[Symbol.iterator]();!(R=(A=N.next()).done);R=!0){var C=A.value;O.push(new p(C,e))}}catch(t){P=!0,S=t}finally{try{!R&&N.return&&N.return()}finally{if(P)throw S}}}var I=[];if(t.textures){var L=!0,k=!1,F=void 0;try{for(var D,j=t.textures[Symbol.iterator]();!(L=(D=j.next()).done);L=!0){var B=D.value,U=O[B.source].texture(b);if(B.sampler){var V=V[B.sampler];U.sampler.minFilter=V.minFilter,U.sampler.magFilter=V.magFilter,U.sampler.wrapS=V.wrapS,U.sampler.wrapT=V.wrapT}I.push(U)}}catch(t){k=!0,F=t}finally{try{!L&&j.return&&j.return()}finally{if(k)throw F}}}function Y(t){return t?I[t.index]:null}var G=[];if(t.materials){var q=!0,H=!1,X=void 0;try{for(var W,K=t.materials[Symbol.iterator]();!(q=(W=K.next()).done);q=!0){var z=W.value,Q=new i.PbrMaterial,J=z.pbrMetallicRoughness||{};switch(Q.baseColorFactor.value=J.baseColorFactor||[1,1,1,1],Q.baseColor.texture=Y(J.baseColorTexture),Q.metallicRoughnessFactor.value=[J.metallicFactor||1,J.roughnessFactor||1],Q.metallicRoughness.texture=Y(J.metallicRoughnessTexture),Q.normal.texture=Y(t.normalTexture),Q.occlusion.texture=Y(t.occlusionTexture),Q.occlusionStrength.value=t.occlusionTexture&&t.occlusionTexture.strength?t.occlusionTexture.strength:1,Q.emissiveFactor.value=z.emissiveFactor||[0,0,0],Q.emissive.texture=Y(t.emissiveTexture),!Q.emissive.texture&&t.emissiveFactor&&(Q.emissive.texture=new u.ColorTexture(1,1,1,1)),z.alphaMode){case"BLEND":case"MASK":Q.state.blend=!0;break;default:Q.state.blend=!1}Q.state.cullFace=!z.doubleSided,G.push(Q)}}catch(t){H=!0,X=t}finally{try{!q&&K.return&&K.return()}finally{if(H)throw X}}}var Z=t.accessors,$=[],tt=!0,et=!1,rt=void 0;try{for(var nt,it=t.meshes[Symbol.iterator]();!(tt=(nt=it.next()).done);tt=!0){var ot=nt.value,at=new _;$.push(at);var ut=!0,st=!1,ct=void 0;try{for(var ft,lt=ot.primitives[Symbol.iterator]();!(ut=(ft=lt.next()).done);ut=!0){var ht=ft.value,dt=null;dt="material"in ht?G[ht.material]:new i.PbrMaterial;var vt=[],_t=0,mt=null,pt=null;for(var yt in ht.attributes){var bt=Z[ht.attributes[yt]],gt=b[bt.bufferView];_t=bt.count;var Mt=new a.PrimitiveAttribute(yt,gt.renderBuffer(this.renderer,c.ARRAY_BUFFER),v(bt.type),bt.componentType,gt.byteStride||0,bt.byteOffset||0);Mt.normalized=bt.normalized||!1,"POSITION"==yt&&(mt=bt.min,pt=bt.max),vt.push(Mt)}var xt=new a.Primitive(vt,_t,ht.mode);if("indices"in ht){var Tt=Z[ht.indices],Et=b[Tt.bufferView];xt.setIndexBuffer(Et.renderBuffer(this.renderer,c.ELEMENT_ARRAY_BUFFER),Tt.byteOffset||0,Tt.componentType),xt.indexType=Tt.componentType,xt.indexByteOffset=Tt.byteOffset||0,xt.elementCount=Tt.count}mt&&pt&&xt.setBounds(mt,pt),at.primitives.push(this.renderer.createRenderPrimitive(xt,dt))}}catch(t){st=!0,ct=t}finally{try{!ut&&lt.return&&lt.return()}finally{if(st)throw ct}}}}catch(t){et=!0,rt=t}finally{try{!tt&&it.return&&it.return()}finally{if(et)throw rt}}var wt=new o.Node,Ot=t.scenes[t.scene],Rt=!0,Pt=!1,St=void 0;try{for(var At,Nt=Ot.nodes[Symbol.iterator]();!(Rt=(At=Nt.next()).done);Rt=!0){var Ct=At.value,It=t.nodes[Ct];wt.addNode(this.processNodes(It,t.nodes,$))}}catch(t){Pt=!0,St=t}finally{try{!Rt&&Nt.return&&Nt.return()}finally{if(Pt)throw St}}return wt}},{key:"processNodes",value:function(t,e,r){var n=new o.Node;if(n.name=t.name,"mesh"in t){var i=r[t.mesh],a=!0,u=!1,s=void 0;try{for(var c,f=i.primitives[Symbol.iterator]();!(a=(c=f.next()).done);a=!0){var l=c.value;n.addRenderPrimitive(l)}}catch(t){u=!0,s=t}finally{try{!a&&f.return&&f.return()}finally{if(u)throw s}}}if(t.matrix?n.matrix=new Float32Array(t.matrix):(t.translation||t.rotation||t.scale)&&(t.translation&&(n.translation=new Float32Array(t.translation)),t.rotation&&(n.rotation=new Float32Array(t.rotation)),t.scale&&(n.scale=new Float32Array(t.scale))),t.children){var h=!0,d=!1,v=void 0;try{for(var _,m=t.children[Symbol.iterator]();!(h=(_=m.next()).done);h=!0){var p=e[_.value];n.addNode(this.processNodes(p,e,r))}}catch(t){d=!0,v=t}finally{try{!h&&m.return&&m.return()}finally{if(d)throw v}}}return n}}]),t}();var _=function t(){s(this,t),this.primitives=[]},m=function(){function t(e,r){s(this,t),this.buffer=r[e.buffer],this.byteOffset=e.byteOffset||0,this.byteLength=e.byteLength||null,this.byteStride=e.byteStride,this._viewPromise=null,this._renderBuffer=null}return n(t,[{key:"dataView",value:function(){var t=this;return this._viewPromise||(this._viewPromise=this.buffer.arrayBuffer().then(function(e){return new DataView(e,t.byteOffset,t.byteLength)})),this._viewPromise}},{key:"renderBuffer",value:function(t,e){return this._renderBuffer||(this._renderBuffer=t.createRenderBuffer(e,this.dataView())),this._renderBuffer}}]),t}(),p=function(){function t(e,r,n){s(this,t),this.json=e,this.baseUrl=r,this._dataPromise=null,this._texture=null,n&&(this._dataPromise=Promise.resolve(n))}return n(t,[{key:"arrayBuffer",value:function(){if(!this._dataPromise){if(h(this.json.uri)){var t=this.json.uri.replace("data:application/octet-stream;base64,",""),e=Uint8Array.from(atob(t),function(t){return t.charCodeAt(0)});return this._dataPromise=Promise.resolve(e.buffer),this._dataPromise}this._dataPromise=fetch(d(this.json.uri,this.baseUrl)).then(function(t){return t.arrayBuffer()})}return this._dataPromise}},{key:"texture",value:function(t){var e=this;if(!this._texture){var r=new Image;if(this._texture=new u.ImageTexture(r),this.json.uri)h(this.json.uri)?r.src=this.json.uri:r.src=""+this.baseUrl+this.json.uri;else t[this.json.bufferView].dataView().then(function(t){var n=new Blob([t],{type:e.json.mimeType});r.src=window.URL.createObjectURL(n)})}return this._texture}}]),t}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.SkyboxNode=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(7),a=r(2),u=r(8);function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function f(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var l=WebGLRenderingContext,h=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.renderOrder=i.RENDER_ORDER.SKY,t.state.depthFunc=l.LEQUAL,t.state.depthMask=!1,t.image=t.defineSampler("diffuse"),t.texCoordScaleOffset=t.defineUniform("texCoordScaleOffset",[1,1,0,0,1,1,0,0],4),t}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"SKYBOX"}},{key:"vertexSource",get:function(){return"\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      // Drop the translation portion of the view matrix\n      view[3].xyz = vec3(0.0, 0.0, 0.0);\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n\n      // Returning the W component for both Z and W forces the geometry depth to\n      // the far plane. When combined with a depth func of LEQUAL this makes the\n      // sky write to any depth fragment that has not been written to yet.\n      return out_vec.xyww;\n    }"}},{key:"fragmentSource",get:function(){return"\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }"}}]),e}();e.SkyboxNode=function(t){function e(t){s(this,e);var r=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r._url=t.url,r._displayMode=t.displayMode||"mono",r._rotationY=t.rotationY||0,r}return f(e,a.Node),n(e,[{key:"onRendererChanged",value:function(t){for(var e=[],r=[],n=0;n<=40;++n)for(var i=n*Math.PI/40,a=Math.sin(i),s=Math.cos(i),c=41*n,f=41*(n+1),d=0;d<=40;++d){var v=2*d*Math.PI/40+this._rotationY,_=Math.sin(v)*a,m=s,p=-Math.cos(v)*a,y=d/40,b=n/40;if(e.push(_,m,p,y,b),n<40&&d<40){var g=c+d,M=f+d;r.push(g,M,g+1,M,M+1,g+1)}}var x=t.createRenderBuffer(l.ARRAY_BUFFER,new Float32Array(e)),T=t.createRenderBuffer(l.ELEMENT_ARRAY_BUFFER,new Uint16Array(r)),E=[new o.PrimitiveAttribute("POSITION",x,3,l.FLOAT,20,0),new o.PrimitiveAttribute("TEXCOORD_0",x,2,l.FLOAT,20,12)],w=new o.Primitive(E,r.length);w.setIndexBuffer(T);var O=new h;switch(O.image.texture=new u.UrlTexture(this._url),this._displayMode){case"mono":O.texCoordScaleOffset.value=[1,1,0,0,1,1,0,0];break;case"stereoTopBottom":O.texCoordScaleOffset.value=[1,.5,0,0,1,.5,0,.5];break;case"stereoLeftRight":O.texCoordScaleOffset.value=[.5,1,0,0,.5,1,.5,0]}var R=t.createRenderPrimitive(w,O);this.addRenderPrimitive(R)}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.VideoNode=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(7),a=r(2),u=r(8);function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function f(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var l=WebGLRenderingContext,h=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.image=t.defineSampler("diffuse"),t.texCoordScaleOffset=t.defineUniform("texCoordScaleOffset",[1,1,0,0,1,1,0,0],4),t}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"VIDEO_PLAYER"}},{key:"vertexSource",get:function(){return"\n    uniform int EYE_INDEX;\n    uniform vec4 texCoordScaleOffset[2];\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX];\n      vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw;\n      vec4 out_vec = proj * view * model * vec4(POSITION, 1.0);\n      return out_vec;\n    }"}},{key:"fragmentSource",get:function(){return"\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    vec4 fragment_main() {\n      return texture2D(diffuse, vTexCoord);\n    }"}}]),e}();e.VideoNode=function(t){function e(t){s(this,e);var r=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return r._video=t.video,r._displayMode=t.displayMode||"mono",r._video_texture=new u.VideoTexture(r._video),r}return f(e,a.Node),n(e,[{key:"onRendererChanged",value:function(t){var e=[0,2,1,0,3,2],r=t.createRenderBuffer(l.ARRAY_BUFFER,new Float32Array([-1,1,0,0,0,1,1,0,1,0,1,-1,0,1,1,-1,-1,0,0,1])),n=t.createRenderBuffer(l.ELEMENT_ARRAY_BUFFER,new Uint16Array(e)),i=[new o.PrimitiveAttribute("POSITION",r,3,l.FLOAT,20,0),new o.PrimitiveAttribute("TEXCOORD_0",r,2,l.FLOAT,20,12)],a=new o.Primitive(i,e.length);a.setIndexBuffer(n),a.setBounds([-1,-1,0],[1,1,.015]);var u=new h;switch(u.image.texture=this._video_texture,this._displayMode){case"mono":u.texCoordScaleOffset.value=[1,1,0,0,1,1,0,0];break;case"stereoTopBottom":u.texCoordScaleOffset.value=[1,.5,0,0,1,.5,0,.5];break;case"stereoLeftRight":u.texCoordScaleOffset.value=[.5,1,0,0,.5,1,.5,0]}var s=t.createRenderPrimitive(a,u);this.addRenderPrimitive(s)}},{key:"aspectRatio",get:function(){var t=this._video.videoWidth,e=this._video.videoHeight;switch(this._displayMode){case"stereoTopBottom":e*=.5;break;case"stereoLeftRight":t*=.5}return e&&t?t/e:1}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Scene=e.WebXRView=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(11),o=r(31),a=r(32),u=r(2),s=r(6);function c(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function f(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var h=e.WebXRView=function(t){function e(t,r){return c(this,e),f(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t?t.projectionMatrix:null,t?t.viewMatrix:null,r&&t?r.getViewport(t):null,t?t.eye:"left"))}return l(e,i.RenderView),e}();e.Scene=function(t){function e(){c(this,e);var t=f(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t._timestamp=-1,t._frameDelta=0,t._statsStanding=!1,t._stats=null,t._statsEnabled=!1,t.enableStats(!0),t._inputRenderer=null,t._resetInputEndFrame=!0,t._lastTimestamp=0,t._hoverFrame=0,t._hoveredNodes=[],t.clear=!0,t}return l(e,u.Node),n(e,[{key:"setRenderer",value:function(t){this._setRenderer(t)}},{key:"loseRenderer",value:function(){this._renderer&&(this._stats=null,this._renderer=null,this._inputRenderer=null)}},{key:"updateInputSources",value:function(t,e){if(t.session.getInputSources){var r=t.session.getInputSources(),n=[],i=this._hoverFrame;this._hoverFrame++;var o=!0,a=!1,u=void 0;try{for(var c,f=r[Symbol.iterator]();!(o=(c=f.next()).done);o=!0){var l=c.value,h=t.getInputPose(l,e);if(h&&(h.gripMatrix&&this.inputRenderer.addController(h.gripMatrix),h.targetRay)){"tracked-pointer"==l.targetRayMode&&this.inputRenderer.addLaserPointer(h.targetRay);var d=this.hitTest(h.targetRay);if(d)this.inputRenderer.addCursor(d.intersection),d.node._hoverFrameId!=i&&d.node.onHoverStart(),d.node._hoverFrameId=this._hoverFrame,n.push(d.node);else{var v=s.vec3.fromValues(h.targetRay.origin.x,h.targetRay.origin.y,h.targetRay.origin.z);s.vec3.add(v,v,[1*h.targetRay.direction.x,1*h.targetRay.direction.y,1*h.targetRay.direction.z]),this.inputRenderer.addCursor(v)}}}}catch(t){a=!0,u=t}finally{try{!o&&f.return&&f.return()}finally{if(a)throw u}}var _=!0,m=!1,p=void 0;try{for(var y,b=this._hoveredNodes[Symbol.iterator]();!(_=(y=b.next()).done);_=!0){var g=y.value;g._hoverFrameId!=this._hoverFrame&&g.onHoverEnd()}}catch(t){m=!0,p=t}finally{try{!_&&b.return&&b.return()}finally{if(m)throw p}}this._hoveredNodes=n}}},{key:"handleSelect",value:function(t,e,r){var n=e.getInputPose(t,r);n&&this.handleSelectPointer(n.targetRay)}},{key:"handleSelectPointer",value:function(t){if(t){var e=this.hitTest(t);e&&e.node.handleSelect()}}},{key:"enableStats",value:function(t){t!=this._statsEnabled&&(this._statsEnabled=t,t?(this._stats=new a.StatsViewer,this._stats.selectable=!0,this.addNode(this._stats),this._statsStanding?this._stats.translation=[0,1.4,-.75]:this._stats.translation=[0,-.3,-.5],this._stats.scale=[.3,.3,.3],s.quat.fromEuler(this._stats.rotation,-45,0,0)):t||this._stats&&(this.removeNode(this._stats),this._stats=null))}},{key:"standingStats",value:function(t){this._statsStanding=t,this._stats&&(this._statsStanding?this._stats.translation=[0,1.4,-.75]:this._stats.translation=[0,-.3,-.5],this._stats.scale=[.3,.3,.3],s.quat.fromEuler(this._stats.rotation,-45,0,0))}},{key:"draw",value:function(t,e,r){var n=new i.RenderView;n.projectionMatrix=t,n.viewMatrix=e,r&&(n.eye=r),this.drawViewArray([n])}},{key:"drawXRFrame",value:function(t,e){if(this._renderer&&e){var r=this._renderer.gl,n=t.session.baseLayer;if(r){r.bindFramebuffer(r.FRAMEBUFFER,n.framebuffer),this.clear&&r.clear(r.COLOR_BUFFER_BIT|r.DEPTH_BUFFER_BIT);var i=[],o=!0,a=!1,u=void 0;try{for(var s,c=e.views[Symbol.iterator]();!(o=(s=c.next()).done);o=!0){var f=s.value;i.push(new h(f,n))}}catch(t){a=!0,u=t}finally{try{!o&&c.return&&c.return()}finally{if(a)throw u}}this.drawViewArray(i)}}}},{key:"drawViewArray",value:function(t){this._renderer&&this._renderer.drawViews(t,this)}},{key:"startFrame",value:function(){var t=this._timestamp;return this._timestamp=performance.now(),this._stats&&this._stats.begin(),this._frameDelta=t>=0?this._timestamp-t:0,this._update(this._timestamp,this._frameDelta),this._frameDelta}},{key:"endFrame",value:function(){this._inputRenderer&&this._resetInputEndFrame&&this._inputRenderer.reset(),this._stats&&this._stats.end()}},{key:"onLoadScene",value:function(t){return Promise.resolve()}},{key:"inputRenderer",get:function(){return this._inputRenderer||(this._inputRenderer=new o.InputRenderer,this.addNode(this._inputRenderer)),this._inputRenderer}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.InputRenderer=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(7),u=r(8);function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function f(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var l=WebGLRenderingContext,h=new Uint8Array([255,255,255,1,255,255,255,2,191,191,191,4,204,204,204,5,219,219,219,7,204,204,204,10,216,216,216,13,210,210,210,17,206,206,206,21,206,206,206,26,206,206,206,31,205,205,205,36,200,200,200,42,201,201,201,47,201,201,201,52,201,201,201,57,201,201,201,61,200,200,200,65,203,203,203,68,238,238,238,135,250,250,250,200,249,249,249,201,249,249,249,201,250,250,250,201,250,250,250,201,249,249,249,201,249,249,249,201,250,250,250,200,238,238,238,135,203,203,203,68,200,200,200,65,201,201,201,61,201,201,201,57,201,201,201,52,201,201,201,47,200,200,200,42,205,205,205,36,206,206,206,31,206,206,206,26,206,206,206,21,210,210,210,17,216,216,216,13,204,204,204,10,219,219,219,7,204,204,204,5,191,191,191,4,255,255,255,2,255,255,255,1]),d=[1,1,1,.25],v=[1,1,1,1],_=[.5,.5,.5,.25],m={controllers:!0,lasers:!0,cursors:!0},p=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.renderOrder=i.RENDER_ORDER.ADDITIVE,t.state.cullFace=!1,t.state.blend=!0,t.state.blendFuncSrc=l.ONE,t.state.blendFuncDst=l.ONE,t.state.depthMask=!1,t.laser=t.defineSampler("diffuse"),t.laser.texture=new u.DataTexture(h,48,1),t.laserColor=t.defineUniform("laserColor",d),t}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"INPUT_LASER"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n    attribute vec2 TEXCOORD_0;\n\n    varying vec2 vTexCoord;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vTexCoord = TEXCOORD_0;\n      return proj * view * model * vec4(POSITION, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return"\n    precision mediump float;\n\n    uniform vec4 laserColor;\n    uniform sampler2D diffuse;\n    varying vec2 vTexCoord;\n\n    const float fadePoint = 0.5335;\n    const float fadeEnd = 0.535;\n\n    vec4 fragment_main() {\n      vec2 uv = vTexCoord;\n      float front_fade_factor = 1.0 - clamp(1.0 - (uv.y - fadePoint) / (1.0 - fadePoint), 0.0, 1.0);\n      float back_fade_factor = clamp((uv.y - fadePoint) / (fadeEnd - fadePoint), 0.0, 1.0);\n      vec4 color = laserColor * texture2D(diffuse, vTexCoord);\n      float opacity = color.a * front_fade_factor * back_fade_factor;\n      return vec4(color.rgb * opacity, opacity);\n    }"}}]),e}(),y="\nattribute vec4 POSITION;\n\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n  vLuminance = POSITION.z;\n  vOpacity = POSITION.w;\n\n  // Billboarded, constant size vertex transform.\n  vec4 screenPos = proj * view * model * vec4(0.0, 0.0, 0.0, 1.0);\n  screenPos /= screenPos.w;\n  screenPos.xy += POSITION.xy;\n  return screenPos;\n}",b="\nprecision mediump float;\n\nuniform vec4 cursorColor;\nvarying float vLuminance;\nvarying float vOpacity;\n\nvec4 fragment_main() {\n  vec3 color = cursorColor.rgb * vLuminance;\n  float opacity = cursorColor.a * vOpacity;\n  return vec4(color * opacity, opacity);\n}",g=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.renderOrder=i.RENDER_ORDER.ADDITIVE,t.state.cullFace=!1,t.state.blend=!0,t.state.blendFuncSrc=l.ONE,t.state.depthMask=!1,t.cursorColor=t.defineUniform("cursorColor",v),t}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"INPUT_CURSOR"}},{key:"vertexSource",get:function(){return y}},{key:"fragmentSource",get:function(){return b}}]),e}(),M=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t.renderOrder=i.RENDER_ORDER.ADDITIVE,t.state.cullFace=!1,t.state.blend=!0,t.state.blendFuncSrc=l.ONE,t.state.depthFunc=l.GEQUAL,t.state.depthMask=!1,t.cursorColor=t.defineUniform("cursorColor",_),t}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"INPUT_CURSOR_2"}},{key:"vertexSource",get:function(){return y}},{key:"fragmentSource",get:function(){return b}}]),e}();e.InputRenderer=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t._maxInputElements=32,t._controllers=[],t._controllerNode=null,t._controllerNodeHandedness=null,t._lasers=null,t._cursors=null,t._activeControllers=0,t._activeLasers=0,t._activeCursors=0,t}return f(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){this._controllers=[],this._controllerNode=null,this._controllerNodeHandedness=null,this._lasers=null,this._cursors=null,this._activeControllers=0,this._activeLasers=0,this._activeCursors=0}},{key:"setControllerMesh",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"right";this._controllerNode=t,this._controllerNode.visible=!1,this.addNode(this._controllerNode),this._controllerNodeHandedness=e}},{key:"addController",value:function(t){if(this._controllerNode){var e=null;this._activeControllers<this._controllers.length?e=this._controllers[this._activeControllers]:(e=this._controllerNode.clone(),this.addNode(e),this._controllers.push(e)),this._activeControllers=(this._activeControllers+1)%this._maxInputElements,e.matrix=t,e.visible=!0}}},{key:"addLaserPointer",value:function(t){!this._lasers&&this._renderer&&(this._lasers=[this._createLaserMesh()],this.addNode(this._lasers[0]));var e=null;this._activeLasers<this._lasers.length?e=this._lasers[this._activeLasers]:(e=this._lasers[0].clone(),this.addNode(e),this._lasers.push(e)),this._activeLasers=(this._activeLasers+1)%this._maxInputElements,e.matrix=t.transformMatrix,e.visible=!0}},{key:"addCursor",value:function(t){!this._cursors&&this._renderer&&(this._cursors=[this._createCursorMesh()],this.addNode(this._cursors[0]));var e=null;this._activeCursors<this._cursors.length?e=this._cursors[this._activeCursors]:(e=this._cursors[0].clone(),this.addNode(e),this._cursors.push(e)),this._activeCursors=(this._activeCursors+1)%this._maxInputElements,e.translation=t,e.visible=!0}},{key:"reset",value:function(t){if(t||(t=m),this._controllers&&t.controllers){var e=!0,r=!1,n=void 0;try{for(var i,o=this._controllers[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){i.value.visible=!1}}catch(t){r=!0,n=t}finally{try{!e&&o.return&&o.return()}finally{if(r)throw n}}this._activeControllers=0}if(this._lasers&&t.lasers){var a=!0,u=!1,s=void 0;try{for(var c,f=this._lasers[Symbol.iterator]();!(a=(c=f.next()).done);a=!0){c.value.visible=!1}}catch(t){u=!0,s=t}finally{try{!a&&f.return&&f.return()}finally{if(u)throw s}}this._activeLasers=0}if(this._cursors&&t.cursors){var l=!0,h=!1,d=void 0;try{for(var v,_=this._cursors[Symbol.iterator]();!(l=(v=_.next()).done);l=!0){v.value.visible=!1}}catch(t){h=!0,d=t}finally{try{!l&&_.return&&_.return()}finally{if(h)throw d}}this._activeCursors=0}}},{key:"_createLaserMesh",value:function(){var t=this._renderer._gl,e=.005,r=[0,e,0,0,1,0,e,-1,0,0,0,-e,0,1,1,0,-e,-1,1,0,e,0,0,0,1,e,0,-1,0,0,-e,0,0,1,1,-e,0,-1,1,0,0,-e,0,0,1,0,-e,-1,0,0,0,e,0,1,1,0,e,-1,1,0,-e,0,0,0,1,-e,0,-1,0,0,e,0,0,1,1,e,0,-1,1,0],n=[0,1,2,1,3,2,4,5,6,5,7,6,8,9,10,9,11,10,12,13,14,13,15,14],i=this._renderer.createRenderBuffer(t.ARRAY_BUFFER,new Float32Array(r)),u=this._renderer.createRenderBuffer(t.ELEMENT_ARRAY_BUFFER,new Uint16Array(n)),s=n.length,c=[new a.PrimitiveAttribute("POSITION",i,3,t.FLOAT,20,0),new a.PrimitiveAttribute("TEXCOORD_0",i,2,t.FLOAT,20,12)],f=new a.Primitive(c,s);f.setIndexBuffer(u);var l=new p,h=this._renderer.createRenderPrimitive(f,l),d=new o.Node;return d.addRenderPrimitive(h),d}},{key:"_createCursorMesh",value:function(){for(var t=this._renderer._gl,e=[],r=[],n=2*Math.PI/16,i=0;i<16;++i){var u=i*n,s=Math.cos(u),c=Math.sin(u);e.push(.004*s,.004*c,1,.9),i>1&&r.push(0,i-1,i)}for(var f=0;f<16;++f){var l=f*n,h=Math.cos(l),d=Math.sin(l);if(e.push(.004*h,.004*d,.5,.75),e.push(.007*h,.007*d,0,0),f>0){var v=16+2*f;r.push(v-2,v-1,v),r.push(v-1,v+1,v)}}r.push(46,47,16),r.push(47,17,16);var _=this._renderer.createRenderBuffer(t.ARRAY_BUFFER,new Float32Array(e)),m=this._renderer.createRenderBuffer(t.ELEMENT_ARRAY_BUFFER,new Uint16Array(r)),p=r.length,y=[new a.PrimitiveAttribute("POSITION",_,4,t.FLOAT,16,0)],b=new a.Primitive(y,p);b.setIndexBuffer(m);var x=new g,T=new M,E=this._renderer.createRenderPrimitive(b,x),w=this._renderer.createRenderPrimitive(b,T),O=new o.Node;return O.addRenderPrimitive(E),O.addRenderPrimitive(w),O}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.StatsViewer=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(7),u=r(33);function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function f(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var l=30,h=90,d=function(t){function e(){return s(this,e),c(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"STATS_VIEWER"}},{key:"vertexSource",get:function(){return"\n    attribute vec3 POSITION;\n    attribute vec3 COLOR_0;\n    varying vec4 vColor;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      vColor = vec4(COLOR_0, 1.0);\n      return proj * view * model * vec4(POSITION, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return"\n    precision mediump float;\n    varying vec4 vColor;\n\n    vec4 fragment_main() {\n      return vColor;\n    }"}}]),e}();function v(t){return.9/l*t-.45}function _(t){return Math.min(t,h)*(.7/h)-.45}var m=window.performance&&performance.now?performance.now.bind(performance):Date.now;e.StatsViewer=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t._performanceMonitoring=!1,t._startTime=m(),t._prevFrameTime=t._startTime,t._prevGraphUpdateTime=t._startTime,t._frames=0,t._fpsAverage=0,t._fpsMin=0,t._fpsStep=t._performanceMonitoring?1e3:250,t._lastSegment=0,t._fpsVertexBuffer=null,t._fpsRenderPrimitive=null,t._fpsNode=null,t._sevenSegmentNode=new u.SevenSegmentText,t._sevenSegmentNode.matrix=new Float32Array([.075,0,0,0,0,.075,0,0,0,0,1,0,-.3625,.3625,.02,1]),t}return f(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){this.clearNodes();for(var e=t.gl,r=[],n=[],i=0;i<l;++i){r.push(v(i),_(0),.02,0,1,1),r.push(v(i+1),_(0),.02,0,1,1),r.push(v(i),_(0),.02,0,1,1),r.push(v(i+1),_(0),.02,0,1,1);var u=4*i;n.push(u,u+3,u+1,u+3,u,u+2)}function s(t,e,i,o,a,u,s,c){var f=r.length/6;r.push(t,e,a,u,s,c),r.push(i,o,a,u,s,c),r.push(t,o,a,u,s,c),r.push(i,e,a,u,s,c),n.push(f,f+1,f+2,f,f+3,f+1)}s(-.5,-.5,.5,.5,0,0,0,.125),s(-.45,-.45,.45,.25,.01,0,0,.4),s(-.45,_(30),.45,_(32),.015,.5,0,.5),s(-.45,_(60),.45,_(62),.015,.2,0,.75),this._fpsVertexBuffer=t.createRenderBuffer(e.ARRAY_BUFFER,new Float32Array(r),e.DYNAMIC_DRAW);var c=t.createRenderBuffer(e.ELEMENT_ARRAY_BUFFER,new Uint16Array(n)),f=[new a.PrimitiveAttribute("POSITION",this._fpsVertexBuffer,3,e.FLOAT,24,0),new a.PrimitiveAttribute("COLOR_0",this._fpsVertexBuffer,3,e.FLOAT,24,12)],h=new a.Primitive(f,n.length);h.setIndexBuffer(c),h.setBounds([-.5,-.5,0],[.5,.5,.015]),this._fpsRenderPrimitive=t.createRenderPrimitive(h,new d),this._fpsNode=new o.Node,this._fpsNode.addRenderPrimitive(this._fpsRenderPrimitive),this.addNode(this._fpsNode),this.addNode(this._sevenSegmentNode)}},{key:"begin",value:function(){this._startTime=m()}},{key:"end",value:function(){var t=m(),e=1e3/(t-this._prevFrameTime);if(this._prevFrameTime=t,this._fpsMin=this._frames?Math.min(this._fpsMin,e):e,this._frames++,t>this._prevGraphUpdateTime+this._fpsStep){var r=t-this._prevGraphUpdateTime;this._fpsAverage=Math.round(1e3/(r/this._frames)),this._updateGraph(this._fpsMin,this._fpsAverage),this._performanceMonitoring&&console.log("Average FPS: "+this._fpsAverage+" Min FPS: "+this._fpsMin),this._prevGraphUpdateTime=t,this._frames=0,this._fpsMin=0}}},{key:"_updateGraph",value:function(t,e){var r,n=(r=t,{r:Math.max(0,Math.min(1,1-r/60)),g:Math.max(0,Math.min(1,(r-15)/(h-15))),b:Math.max(0,Math.min(1,(r-15)/(h-15)))}),i=_(t-1),o=_(e+1),a=[v(this._lastSegment),o,.02,n.r,n.g,n.b,v(this._lastSegment+1),o,.02,n.r,n.g,n.b,v(this._lastSegment),i,.02,n.r,n.g,n.b,v(this._lastSegment+1),i,.02,n.r,n.g,n.b];n.r=.2,n.g=1,n.b=.2,this._lastSegment==l-1?(this._renderer.updateRenderBuffer(this._fpsVertexBuffer,new Float32Array(a),24*this._lastSegment*4),a=[v(0),_(h),.02,n.r,n.g,n.b,v(.25),_(h),.02,n.r,n.g,n.b,v(0),_(0),.02,n.r,n.g,n.b,v(.25),_(0),.02,n.r,n.g,n.b],this._renderer.updateRenderBuffer(this._fpsVertexBuffer,new Float32Array(a),0)):(a.push(v(this._lastSegment+1),_(h),.02,n.r,n.g,n.b,v(this._lastSegment+1.25),_(h),.02,n.r,n.g,n.b,v(this._lastSegment+1),_(0),.02,n.r,n.g,n.b,v(this._lastSegment+1.25),_(0),.02,n.r,n.g,n.b),this._renderer.updateRenderBuffer(this._fpsVertexBuffer,new Float32Array(a),24*this._lastSegment*4)),this._lastSegment=(this._lastSegment+1)%l,this._sevenSegmentNode.text=this._fpsAverage+" FP5"}},{key:"performanceMonitoring",get:function(){return this._performanceMonitoring},set:function(t){this._performanceMonitoring=t,this._fpsStep=t?1e3:250}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.SevenSegmentText=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(3),o=r(2),a=r(7);function u(t){if(Array.isArray(t)){for(var e=0,r=Array(t.length);e<t.length;e++)r[e]=t[e];return r}return Array.from(t)}function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function f(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var l=function(t){function e(){return s(this,e),c(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return f(e,i.Material),n(e,[{key:"materialName",get:function(){return"SEVEN_SEGMENT_TEXT"}},{key:"vertexSource",get:function(){return"\n    attribute vec2 POSITION;\n\n    vec4 vertex_main(mat4 proj, mat4 view, mat4 model) {\n      return proj * view * model * vec4(POSITION, 0.0, 1.0);\n    }"}},{key:"fragmentSource",get:function(){return"\n    precision mediump float;\n    const vec4 color = vec4(0.0, 1.0, 0.0, 1.0);\n\n    vec4 fragment_main() {\n      return color;\n    }"}}]),e}();e.SevenSegmentText=function(t){function e(){s(this,e);var t=c(this,(e.__proto__||Object.getPrototypeOf(e)).call(this));return t._text="",t._charNodes=[],t}return f(e,o.Node),n(e,[{key:"onRendererChanged",value:function(t){this.clearNodes(),this._charNodes=[];var e=[],r={},n=[];function i(t,n,i,o,a){var u=e.length/2;e.push(n,i,o,i,o,a,n,a),r[t]=[u,u+2,u+1,u,u+3,u+2]}var o={};function s(t,e){for(var i={character:t,offset:2*n.length,count:0},a=0;a<e.length;++a){var s=e[a],c=r[s];i.count+=c.length,n.push.apply(n,u(c))}o[t]=i}i(0,-1,1,.5,.75),i(1,-1,.125,.5,-.125),i(2,-1,-.75,.5,-1),i(3,-1,1,-.75,-.125),i(4,.25,1,.5,-.125),i(5,-1,.125,-.75,-1),i(6,.25,.125,.5,-1),s("0",[0,2,3,4,5,6]),s("1",[4,6]),s("2",[0,1,2,4,5]),s("3",[0,1,2,4,6]),s("4",[1,3,4,6]),s("5",[0,1,2,3,6]),s("6",[0,1,2,3,5,6]),s("7",[0,4,6]),s("8",[0,1,2,3,4,5,6]),s("9",[0,1,2,3,4,6]),s("A",[0,1,3,4,5,6]),s("B",[1,2,3,5,6]),s("C",[0,2,3,5]),s("D",[1,2,4,5,6]),s("E",[0,1,2,4,6]),s("F",[0,1,3,5]),s("P",[0,1,3,4,5]),s("-",[1]),s(" ",[]),s("_",[2]);var c=t.gl,f=t.createRenderBuffer(c.ARRAY_BUFFER,new Float32Array(e)),h=t.createRenderBuffer(c.ELEMENT_ARRAY_BUFFER,new Uint16Array(n)),d=[new a.PrimitiveAttribute("POSITION",f,2,c.FLOAT,8,0)],v=new a.Primitive(d,n.length);v.setIndexBuffer(h);var _=new l;for(var m in this._charPrimitives={},o){var p=o[m];v.elementCount=p.count,v.indexByteOffset=p.offset,this._charPrimitives[m]=t.createRenderPrimitive(v,_)}this.text=this._text}},{key:"text",get:function(){return this._text},set:function(t){this._text=t;for(var e=0,r=null;e<t.length;++e)if(r=t[e]in this._charPrimitives?this._charPrimitives[t[e]]:this._charPrimitives._,this._charNodes.length<=e){var n=new o.Node;n.addRenderPrimitive(r);var i=2*e;n.translation=[i,0,0],this._charNodes.push(n),this.addNode(n)}else this._charNodes[e].clearRenderPrimitives(),this._charNodes[e].addRenderPrimitive(r),this._charNodes[e].visible=!0;for(;e<this._charNodes.length;++e)this._charNodes[e].visible=!1}}]),e}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.FallbackHelper=void 0;var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),i=r(6);e.FallbackHelper=function(){function t(e,r){var n=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.scene=e,this.gl=r,this._emulateStage=!1,this.lookYaw=0,this.lookPitch=0,this.viewMatrix=i.mat4.create();var o=i.mat4.create();function a(){r.canvas.width=r.canvas.offsetWidth*window.devicePixelRatio,r.canvas.height=r.canvas.offsetHeight*window.devicePixelRatio,i.mat4.perspective(o,.4*Math.PI,r.canvas.width/r.canvas.height,.1,1e3),r.viewport(0,0,r.drawingBufferWidth,r.drawingBufferHeight)}this.projectionMatrix=o,i.mat4.identity(this.viewMatrix),window.addEventListener("resize",a),a();var u=r.canvas,s=0,c=0;u.addEventListener("touchstart",function(t){2==t.touches.length&&(s=t.touches[1].pageX,c=t.touches[1].pageY)}),u.addEventListener("touchmove",function(t){2==t.touches.length&&(n.onLook(t.touches[1].pageX-s,t.touches[1].pageY-c),s=t.touches[1].pageX,c=t.touches[1].pageY)}),u.addEventListener("mousemove",function(t){2&t.buttons&&n.onLook(t.movementX,t.movementY)}),u.addEventListener("contextmenu",function(t){t.preventDefault()}),this.boundOnFrame=this.onFrame.bind(this),window.requestAnimationFrame(this.boundOnFrame)}return n(t,[{key:"onLook",value:function(t,e){this.lookYaw+=.0025*t,this.lookPitch+=.0025*e,this.lookPitch<.5*-Math.PI&&(this.lookPitch=.5*-Math.PI),this.lookPitch>.5*Math.PI&&(this.lookPitch=.5*Math.PI),this.updateView()}},{key:"onFrame",value:function(t){var e=this.gl;window.requestAnimationFrame(this.boundOnFrame),this.scene.startFrame(),e.clear(e.COLOR_BUFFER_BIT|e.DEPTH_BUFFER_BIT),this.scene.draw(this.projectionMatrix,this.viewMatrix),this.scene.endFrame()}},{key:"updateView",value:function(){i.mat4.identity(this.viewMatrix),i.mat4.rotateX(this.viewMatrix,this.viewMatrix,-this.lookPitch),i.mat4.rotateY(this.viewMatrix,this.viewMatrix,-this.lookYaw),this._emulateStage&&i.mat4.translate(this.viewMatrix,this.viewMatrix,[0,-1.6,0])}},{key:"emulateStage",get:function(){return this._emulateStage},set:function(t){this._emulateStage=t,this.updateView()}}]),t}()},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();var i=null;function o(){if(!i){i={};for(var t=(window.location.search.substring(1)||window.location.hash.substring(1)).split("&"),e=0;e<t.length;e++){var r=t[e].split("=");i[r[0].toLowerCase()]=decodeURIComponent(r[1])}}}window.onhashchange=function(){i=null};e.QueryArgs=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t)}return n(t,null,[{key:"getString",value:function(t,e){o();var r=t.toLowerCase();return r in i?i[r]:e}},{key:"getInt",value:function(t,e){o();var r=t.toLowerCase();return r in i?parseInt(i[r],10):e}},{key:"getFloat",value:function(t,e){o();var r=t.toLowerCase();return r in i?parseFloat(i[r]):e}},{key:"getBool",value:function(t,e){o();var r=t.toLowerCase();return r in i?0!=parseInt(i[r],10):e}}]),t}()}])});
\ No newline at end of file
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/package.json b/third_party/webxr_test_pages/webxr-samples/js/cottontail/package.json
deleted file mode 100644
index 41ce01b..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/package.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "name": "cottontail",
-  "version": "0.0.1",
-  "description": "Minimalist WebXR rendering framework",
-  "license": "MIT",
-  "readme": "README.md",
-  "babel": {
-    "presets": [
-      "env"
-    ]
-  },
-  "scripts": {
-    "build": "webpack",
-    "build-debug": "webpack --config webpack.config.debug.js",
-    "build-all": "eslint -c .eslintrc.json src && webpack && webpack --config webpack.config.debug.js",
-    "watch": "webpack --watch",
-    "watch-debug": "webpack --config webpack.config.debug.js --watch",
-    "lint": "eslint -c .eslintrc.json src"
-  },
-  "devDependencies": {
-    "babel-core": "^6.26.2",
-    "babel-loader": "^7.1.4",
-    "babel-preset-env": "^1.6.1",
-    "eslint": "^4.19.1",
-    "eslint-config-google": "^0.9.1",
-    "gl-matrix": "^2.5.1",
-    "webpack": "^4.29.0",
-    "webpack-cli": "^3.2.1"
-  }
-}
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/math/gl-matrix.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/math/gl-matrix.js
index fbd22d39..4cb668a7 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/math/gl-matrix.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/math/gl-matrix.js
@@ -18,16 +18,16 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
-import * as glMatrix from '../../node_modules/gl-matrix/src/gl-matrix/common.js';
-import * as mat2 from '../../node_modules/gl-matrix/src/gl-matrix/mat2.js';
-import * as mat2d from '../../node_modules/gl-matrix/src/gl-matrix/mat2d.js';
-import * as mat3 from '../../node_modules/gl-matrix/src/gl-matrix/mat3.js';
-import * as mat4 from '../../node_modules/gl-matrix/src/gl-matrix/mat4.js';
-import * as quat from '../../node_modules/gl-matrix/src/gl-matrix/quat.js';
-import * as quat2 from '../../node_modules/gl-matrix/src/gl-matrix/quat2.js';
-import * as vec2 from '../../node_modules/gl-matrix/src/gl-matrix/vec2.js';
-import * as vec3 from '../../node_modules/gl-matrix/src/gl-matrix/vec3.js';
-import * as vec4 from '../../node_modules/gl-matrix/src/gl-matrix/vec4.js';
+import * as glMatrix from '../../../third-party/gl-matrix/common.js';
+import * as mat2 from '../../../third-party/gl-matrix/mat2.js';
+import * as mat2d from '../../../third-party/gl-matrix/mat2d.js';
+import * as mat3 from '../../../third-party/gl-matrix/mat3.js';
+import * as mat4 from '../../../third-party/gl-matrix/mat4.js';
+import * as quat from '../../../third-party/gl-matrix/quat.js';
+import * as quat2 from '../../../third-party/gl-matrix/quat2.js';
+import * as vec2 from '../../../third-party/gl-matrix/vec2.js';
+import * as vec3 from '../../../third-party/gl-matrix/vec3.js';
+import * as vec4 from '../../../third-party/gl-matrix/vec4.js';
 
 export {
   glMatrix,
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
index ba60b82..bb817d32 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
@@ -25,7 +25,7 @@
 import {vec3, quat} from '../math/gl-matrix.js';
 
 export class WebXRView extends RenderView {
-  constructor(view, layer) {
+  constructor(view, pose, layer) {
     super(
       view ? view.projectionMatrix : null,
       view ? view.viewMatrix : null,
@@ -79,7 +79,7 @@
 
   // Helper function that automatically adds the appropriate visual elements for
   // all input sources.
-  updateInputSources(frame, frameOfRef) {
+  updateInputSources(frame, refSpace) {
     // FIXME: Check for the existence of the API first. This check should be
     // removed once the input API is part of the official spec.
     if (!frame.session.getInputSources) {
@@ -93,7 +93,7 @@
     this._hoverFrame++;
 
     for (let inputSource of inputSources) {
-      let inputPose = frame.getInputPose(inputSource, frameOfRef);
+      let inputPose = frame.getInputPose(inputSource, refSpace);
 
       if (!inputPose) {
         continue;
@@ -158,8 +158,8 @@
     this._hoveredNodes = newHoveredNodes;
   }
 
-  handleSelect(inputSource, frame, frameOfRef) {
-    let inputPose = frame.getInputPose(inputSource, frameOfRef);
+  handleSelect(inputSource, frame, refSpace) {
+    let inputPose = frame.getInputPose(inputSource, refSpace);
 
     if (!inputPose) {
       return;
@@ -254,7 +254,7 @@
 
     let views = [];
     for (let view of pose.views) {
-      views.push(new WebXRView(view, layer));
+      views.push(new WebXRView(view, pose, layer));
     }
 
     this.drawViewArray(views);
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.debug.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.debug.js
deleted file mode 100644
index 6adb055..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.debug.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-'use strict';
-
-let fs = require('fs');
-let path = require('path');
-let webpack = require('webpack');
-
-// Read the LICENSE file to append to the generated file.
-let header = fs.readFileSync('LICENSE.md', { encoding: 'utf8' });
-
-module.exports = {
-  entry: './src/cottontail.js',
-  output: {
-    path: path.join(__dirname, 'build'),
-    filename: 'cottontail.debug.js',
-    libraryTarget: 'umd'
-  },
-  devtool: 'source-map',
-  module: {
-    rules: [
-      {
-        test: /\.js$/,
-        exclude: /node_modules/,
-        use: {
-          loader: 'babel-loader'
-        }
-      }
-    ]
-  },
-  plugins: [
-    new webpack.BannerPlugin({ banner: header, entryOnly: true }),
-  ],
-  mode: "development"
-};
-
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.js
deleted file mode 100644
index 8268697e..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/webpack.config.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-'use strict';
-
-let fs = require('fs');
-let path = require('path');
-let webpack = require('webpack');
-
-// Read the LICENSE file to append to the generated file.
-let header = fs.readFileSync('LICENSE.md', { encoding: 'utf8' });
-
-module.exports = {
-  entry: './src/cottontail.js',
-  output: {
-    path: path.join(__dirname, 'build'),
-    filename: 'cottontail.js',
-    libraryTarget: 'umd'
-  },
-  module: {
-    rules: [
-      {
-        test: /\.js$/,
-        exclude: /node_modules/,
-        use: {
-          loader: 'babel-loader'
-        }
-      }
-    ]
-  },
-  plugins: [
-    new webpack.BannerPlugin({ banner: header, entryOnly: true }),
-  ],
-  mode: "production"
-};
-
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/LICENSE b/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/LICENSE
new file mode 100644
index 0000000..01111932
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2014, Google Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/README.chromium b/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/README.chromium
new file mode 100644
index 0000000..3f7c058
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/README.chromium
@@ -0,0 +1,15 @@
+Name: dat.gui
+Short Name: dat.gui
+URL: https://github.com/dataarts/dat.gui
+Version: 0.7.1
+Revision: 1b18f7227e56c8b5071337732342101501b9fa95
+Date: 2018-05-05
+License: Apache 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+dat.gui is a lightweight controller library for JavaScript.
+
+Local Modifications:
+This copy only contains the build/dat.gui.min.js and LICENSE files.
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui.min.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/dat.gui.min.js
similarity index 100%
rename from third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui.min.js
rename to third_party/webxr_test_pages/webxr-samples/js/third-party/dat.gui/dat.gui.min.js
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/LICENSE b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/LICENSE
new file mode 100644
index 0000000..a2618bf
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/README.chromium b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/README.chromium
new file mode 100644
index 0000000..132b1aa
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/README.chromium
@@ -0,0 +1,15 @@
+Name: gl-matrix
+Short Name: gl-matrix
+URL: https://github.com/toji/gl-matrix
+Version: 2.5.1
+Revision: 168577e351366a8473be1a7c287a4876e0dfac77
+Date: 2018-04-21
+License: MIT
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Javascript Matrix and Vector library for High Performance WebGL apps http://glmatrix.net
+
+Local Modifications:
+This copy contains only the src/gl-matrix/ JS files and the LICENSE.md file.
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/common.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/common.js
new file mode 100644
index 0000000..a67bac7
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/common.js
@@ -0,0 +1,62 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+/**
+ * Common utilities
+ * @module glMatrix
+ */
+
+// Configuration Constants
+export const EPSILON = 0.000001;
+export let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array;
+export const RANDOM = Math.random;
+
+/**
+ * Sets the type of array used when creating new vectors and matrices
+ *
+ * @param {Type} type Array type, such as Float32Array or Array
+ */
+export function setMatrixArrayType(type) {
+  ARRAY_TYPE = type;
+}
+
+const degree = Math.PI / 180;
+
+/**
+ * Convert Degree To Radian
+ *
+ * @param {Number} a Angle in Degrees
+ */
+export function toRadian(a) {
+  return a * degree;
+}
+
+/**
+ * Tests whether or not the arguments have approximately the same value, within an absolute
+ * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
+ * than or equal to 1.0, and a relative tolerance is used for larger values)
+ *
+ * @param {Number} a The first number to test.
+ * @param {Number} b The second number to test.
+ * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
+ */
+export function equals(a, b) {
+  return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b));
+}
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2.js
new file mode 100644
index 0000000..1519173
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2.js
@@ -0,0 +1,433 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js"
+
+/**
+ * 2x2 Matrix
+ * @module mat2
+ */
+
+/**
+ * Creates a new identity mat2
+ *
+ * @returns {mat2} a new 2x2 matrix
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  return out;
+}
+
+/**
+ * Creates a new mat2 initialized with values from an existing matrix
+ *
+ * @param {mat2} a matrix to clone
+ * @returns {mat2} a new 2x2 matrix
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Copy the values from one mat2 to another
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the source matrix
+ * @returns {mat2} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Set a mat2 to the identity matrix
+ *
+ * @param {mat2} out the receiving matrix
+ * @returns {mat2} out
+ */
+export function identity(out) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  return out;
+}
+
+/**
+ * Create a new mat2 with the given values
+ *
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m10 Component in column 1, row 0 position (index 2)
+ * @param {Number} m11 Component in column 1, row 1 position (index 3)
+ * @returns {mat2} out A new 2x2 matrix
+ */
+export function fromValues(m00, m01, m10, m11) {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m10;
+  out[3] = m11;
+  return out;
+}
+
+/**
+ * Set the components of a mat2 to the given values
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m10 Component in column 1, row 0 position (index 2)
+ * @param {Number} m11 Component in column 1, row 1 position (index 3)
+ * @returns {mat2} out
+ */
+export function set(out, m00, m01, m10, m11) {
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m10;
+  out[3] = m11;
+  return out;
+}
+
+/**
+ * Transpose the values of a mat2
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the source matrix
+ * @returns {mat2} out
+ */
+export function transpose(out, a) {
+  // If we are transposing ourselves we can skip a few steps but have to cache
+  // some values
+  if (out === a) {
+    let a1 = a[1];
+    out[1] = a[2];
+    out[2] = a1;
+  } else {
+    out[0] = a[0];
+    out[1] = a[2];
+    out[2] = a[1];
+    out[3] = a[3];
+  }
+
+  return out;
+}
+
+/**
+ * Inverts a mat2
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the source matrix
+ * @returns {mat2} out
+ */
+export function invert(out, a) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+
+  // Calculate the determinant
+  let det = a0 * a3 - a2 * a1;
+
+  if (!det) {
+    return null;
+  }
+  det = 1.0 / det;
+
+  out[0] =  a3 * det;
+  out[1] = -a1 * det;
+  out[2] = -a2 * det;
+  out[3] =  a0 * det;
+
+  return out;
+}
+
+/**
+ * Calculates the adjugate of a mat2
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the source matrix
+ * @returns {mat2} out
+ */
+export function adjoint(out, a) {
+  // Caching this value is nessecary if out == a
+  let a0 = a[0];
+  out[0] =  a[3];
+  out[1] = -a[1];
+  out[2] = -a[2];
+  out[3] =  a0;
+
+  return out;
+}
+
+/**
+ * Calculates the determinant of a mat2
+ *
+ * @param {mat2} a the source matrix
+ * @returns {Number} determinant of a
+ */
+export function determinant(a) {
+  return a[0] * a[3] - a[2] * a[1];
+}
+
+/**
+ * Multiplies two mat2's
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the first operand
+ * @param {mat2} b the second operand
+ * @returns {mat2} out
+ */
+export function multiply(out, a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
+  out[0] = a0 * b0 + a2 * b1;
+  out[1] = a1 * b0 + a3 * b1;
+  out[2] = a0 * b2 + a2 * b3;
+  out[3] = a1 * b2 + a3 * b3;
+  return out;
+}
+
+/**
+ * Rotates a mat2 by the given angle
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat2} out
+ */
+export function rotate(out, a, rad) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  out[0] = a0 *  c + a2 * s;
+  out[1] = a1 *  c + a3 * s;
+  out[2] = a0 * -s + a2 * c;
+  out[3] = a1 * -s + a3 * c;
+  return out;
+}
+
+/**
+ * Scales the mat2 by the dimensions in the given vec2
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the matrix to rotate
+ * @param {vec2} v the vec2 to scale the matrix by
+ * @returns {mat2} out
+ **/
+export function scale(out, a, v) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let v0 = v[0], v1 = v[1];
+  out[0] = a0 * v0;
+  out[1] = a1 * v0;
+  out[2] = a2 * v1;
+  out[3] = a3 * v1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a given angle
+ * This is equivalent to (but much faster than):
+ *
+ *     mat2.identity(dest);
+ *     mat2.rotate(dest, dest, rad);
+ *
+ * @param {mat2} out mat2 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat2} out
+ */
+export function fromRotation(out, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  out[0] = c;
+  out[1] = s;
+  out[2] = -s;
+  out[3] = c;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector scaling
+ * This is equivalent to (but much faster than):
+ *
+ *     mat2.identity(dest);
+ *     mat2.scale(dest, dest, vec);
+ *
+ * @param {mat2} out mat2 receiving operation result
+ * @param {vec2} v Scaling vector
+ * @returns {mat2} out
+ */
+export function fromScaling(out, v) {
+  out[0] = v[0];
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = v[1];
+  return out;
+}
+
+/**
+ * Returns a string representation of a mat2
+ *
+ * @param {mat2} a matrix to represent as a string
+ * @returns {String} string representation of the matrix
+ */
+export function str(a) {
+  return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
+}
+
+/**
+ * Returns Frobenius norm of a mat2
+ *
+ * @param {mat2} a the matrix to calculate Frobenius norm of
+ * @returns {Number} Frobenius norm
+ */
+export function frob(a) {
+  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2)))
+}
+
+/**
+ * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
+ * @param {mat2} L the lower triangular matrix
+ * @param {mat2} D the diagonal matrix
+ * @param {mat2} U the upper triangular matrix
+ * @param {mat2} a the input matrix to factorize
+ */
+
+export function LDU(L, D, U, a) {
+  L[2] = a[2]/a[0];
+  U[0] = a[0];
+  U[1] = a[1];
+  U[3] = a[3] - L[2] * U[1];
+  return [L, D, U];
+}
+
+/**
+ * Adds two mat2's
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the first operand
+ * @param {mat2} b the second operand
+ * @returns {mat2} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  return out;
+}
+
+/**
+ * Subtracts matrix b from matrix a
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the first operand
+ * @param {mat2} b the second operand
+ * @returns {mat2} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  out[3] = a[3] - b[3];
+  return out;
+}
+
+/**
+ * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {mat2} a The first matrix.
+ * @param {mat2} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
+}
+
+/**
+ * Returns whether or not the matrices have approximately the same elements in the same position.
+ *
+ * @param {mat2} a The first matrix.
+ * @param {mat2} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
+}
+
+/**
+ * Multiply each element of the matrix by a scalar.
+ *
+ * @param {mat2} out the receiving matrix
+ * @param {mat2} a the matrix to scale
+ * @param {Number} b amount to scale the matrix's elements by
+ * @returns {mat2} out
+ */
+export function multiplyScalar(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  return out;
+}
+
+/**
+ * Adds two mat2's after multiplying each element of the second operand by a scalar value.
+ *
+ * @param {mat2} out the receiving vector
+ * @param {mat2} a the first operand
+ * @param {mat2} b the second operand
+ * @param {Number} scale the amount to scale b's elements by before adding
+ * @returns {mat2} out
+ */
+export function multiplyScalarAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  out[3] = a[3] + (b[3] * scale);
+  return out;
+}
+
+/**
+ * Alias for {@link mat2.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link mat2.subtract}
+ * @function
+ */
+export const sub = subtract;
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2d.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2d.js
new file mode 100644
index 0000000..b6dc525
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat2d.js
@@ -0,0 +1,466 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * 2x3 Matrix
+ * @module mat2d
+ *
+ * @description
+ * A mat2d contains six elements defined as:
+ * <pre>
+ * [a, c, tx,
+ *  b, d, ty]
+ * </pre>
+ * This is a short form for the 3x3 matrix:
+ * <pre>
+ * [a, c, tx,
+ *  b, d, ty,
+ *  0, 0, 1]
+ * </pre>
+ * The last row is ignored so the array is shorter and operations are faster.
+ */
+
+/**
+ * Creates a new identity mat2d
+ *
+ * @returns {mat2d} a new 2x3 matrix
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(6);
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  out[4] = 0;
+  out[5] = 0;
+  return out;
+}
+
+/**
+ * Creates a new mat2d initialized with values from an existing matrix
+ *
+ * @param {mat2d} a matrix to clone
+ * @returns {mat2d} a new 2x3 matrix
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(6);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  return out;
+}
+
+/**
+ * Copy the values from one mat2d to another
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the source matrix
+ * @returns {mat2d} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  return out;
+}
+
+/**
+ * Set a mat2d to the identity matrix
+ *
+ * @param {mat2d} out the receiving matrix
+ * @returns {mat2d} out
+ */
+export function identity(out) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  out[4] = 0;
+  out[5] = 0;
+  return out;
+}
+
+/**
+ * Create a new mat2d with the given values
+ *
+ * @param {Number} a Component A (index 0)
+ * @param {Number} b Component B (index 1)
+ * @param {Number} c Component C (index 2)
+ * @param {Number} d Component D (index 3)
+ * @param {Number} tx Component TX (index 4)
+ * @param {Number} ty Component TY (index 5)
+ * @returns {mat2d} A new mat2d
+ */
+export function fromValues(a, b, c, d, tx, ty) {
+  let out = new glMatrix.ARRAY_TYPE(6);
+  out[0] = a;
+  out[1] = b;
+  out[2] = c;
+  out[3] = d;
+  out[4] = tx;
+  out[5] = ty;
+  return out;
+}
+
+/**
+ * Set the components of a mat2d to the given values
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {Number} a Component A (index 0)
+ * @param {Number} b Component B (index 1)
+ * @param {Number} c Component C (index 2)
+ * @param {Number} d Component D (index 3)
+ * @param {Number} tx Component TX (index 4)
+ * @param {Number} ty Component TY (index 5)
+ * @returns {mat2d} out
+ */
+export function set(out, a, b, c, d, tx, ty) {
+  out[0] = a;
+  out[1] = b;
+  out[2] = c;
+  out[3] = d;
+  out[4] = tx;
+  out[5] = ty;
+  return out;
+}
+
+/**
+ * Inverts a mat2d
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the source matrix
+ * @returns {mat2d} out
+ */
+export function invert(out, a) {
+  let aa = a[0], ab = a[1], ac = a[2], ad = a[3];
+  let atx = a[4], aty = a[5];
+
+  let det = aa * ad - ab * ac;
+  if(!det){
+    return null;
+  }
+  det = 1.0 / det;
+
+  out[0] = ad * det;
+  out[1] = -ab * det;
+  out[2] = -ac * det;
+  out[3] = aa * det;
+  out[4] = (ac * aty - ad * atx) * det;
+  out[5] = (ab * atx - aa * aty) * det;
+  return out;
+}
+
+/**
+ * Calculates the determinant of a mat2d
+ *
+ * @param {mat2d} a the source matrix
+ * @returns {Number} determinant of a
+ */
+export function determinant(a) {
+  return a[0] * a[3] - a[1] * a[2];
+}
+
+/**
+ * Multiplies two mat2d's
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the first operand
+ * @param {mat2d} b the second operand
+ * @returns {mat2d} out
+ */
+export function multiply(out, a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
+  out[0] = a0 * b0 + a2 * b1;
+  out[1] = a1 * b0 + a3 * b1;
+  out[2] = a0 * b2 + a2 * b3;
+  out[3] = a1 * b2 + a3 * b3;
+  out[4] = a0 * b4 + a2 * b5 + a4;
+  out[5] = a1 * b4 + a3 * b5 + a5;
+  return out;
+}
+
+/**
+ * Rotates a mat2d by the given angle
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat2d} out
+ */
+export function rotate(out, a, rad) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  out[0] = a0 *  c + a2 * s;
+  out[1] = a1 *  c + a3 * s;
+  out[2] = a0 * -s + a2 * c;
+  out[3] = a1 * -s + a3 * c;
+  out[4] = a4;
+  out[5] = a5;
+  return out;
+}
+
+/**
+ * Scales the mat2d by the dimensions in the given vec2
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the matrix to translate
+ * @param {vec2} v the vec2 to scale the matrix by
+ * @returns {mat2d} out
+ **/
+export function scale(out, a, v) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
+  let v0 = v[0], v1 = v[1];
+  out[0] = a0 * v0;
+  out[1] = a1 * v0;
+  out[2] = a2 * v1;
+  out[3] = a3 * v1;
+  out[4] = a4;
+  out[5] = a5;
+  return out;
+}
+
+/**
+ * Translates the mat2d by the dimensions in the given vec2
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the matrix to translate
+ * @param {vec2} v the vec2 to translate the matrix by
+ * @returns {mat2d} out
+ **/
+export function translate(out, a, v) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
+  let v0 = v[0], v1 = v[1];
+  out[0] = a0;
+  out[1] = a1;
+  out[2] = a2;
+  out[3] = a3;
+  out[4] = a0 * v0 + a2 * v1 + a4;
+  out[5] = a1 * v0 + a3 * v1 + a5;
+  return out;
+}
+
+/**
+ * Creates a matrix from a given angle
+ * This is equivalent to (but much faster than):
+ *
+ *     mat2d.identity(dest);
+ *     mat2d.rotate(dest, dest, rad);
+ *
+ * @param {mat2d} out mat2d receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat2d} out
+ */
+export function fromRotation(out, rad) {
+  let s = Math.sin(rad), c = Math.cos(rad);
+  out[0] = c;
+  out[1] = s;
+  out[2] = -s;
+  out[3] = c;
+  out[4] = 0;
+  out[5] = 0;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector scaling
+ * This is equivalent to (but much faster than):
+ *
+ *     mat2d.identity(dest);
+ *     mat2d.scale(dest, dest, vec);
+ *
+ * @param {mat2d} out mat2d receiving operation result
+ * @param {vec2} v Scaling vector
+ * @returns {mat2d} out
+ */
+export function fromScaling(out, v) {
+  out[0] = v[0];
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = v[1];
+  out[4] = 0;
+  out[5] = 0;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector translation
+ * This is equivalent to (but much faster than):
+ *
+ *     mat2d.identity(dest);
+ *     mat2d.translate(dest, dest, vec);
+ *
+ * @param {mat2d} out mat2d receiving operation result
+ * @param {vec2} v Translation vector
+ * @returns {mat2d} out
+ */
+export function fromTranslation(out, v) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  out[4] = v[0];
+  out[5] = v[1];
+  return out;
+}
+
+/**
+ * Returns a string representation of a mat2d
+ *
+ * @param {mat2d} a matrix to represent as a string
+ * @returns {String} string representation of the matrix
+ */
+export function str(a) {
+  return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
+          a[3] + ', ' + a[4] + ', ' + a[5] + ')';
+}
+
+/**
+ * Returns Frobenius norm of a mat2d
+ *
+ * @param {mat2d} a the matrix to calculate Frobenius norm of
+ * @returns {Number} Frobenius norm
+ */
+export function frob(a) {
+  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1))
+}
+
+/**
+ * Adds two mat2d's
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the first operand
+ * @param {mat2d} b the second operand
+ * @returns {mat2d} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  out[4] = a[4] + b[4];
+  out[5] = a[5] + b[5];
+  return out;
+}
+
+/**
+ * Subtracts matrix b from matrix a
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the first operand
+ * @param {mat2d} b the second operand
+ * @returns {mat2d} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  out[3] = a[3] - b[3];
+  out[4] = a[4] - b[4];
+  out[5] = a[5] - b[5];
+  return out;
+}
+
+/**
+ * Multiply each element of the matrix by a scalar.
+ *
+ * @param {mat2d} out the receiving matrix
+ * @param {mat2d} a the matrix to scale
+ * @param {Number} b amount to scale the matrix's elements by
+ * @returns {mat2d} out
+ */
+export function multiplyScalar(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  out[4] = a[4] * b;
+  out[5] = a[5] * b;
+  return out;
+}
+
+/**
+ * Adds two mat2d's after multiplying each element of the second operand by a scalar value.
+ *
+ * @param {mat2d} out the receiving vector
+ * @param {mat2d} a the first operand
+ * @param {mat2d} b the second operand
+ * @param {Number} scale the amount to scale b's elements by before adding
+ * @returns {mat2d} out
+ */
+export function multiplyScalarAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  out[3] = a[3] + (b[3] * scale);
+  out[4] = a[4] + (b[4] * scale);
+  out[5] = a[5] + (b[5] * scale);
+  return out;
+}
+
+/**
+ * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {mat2d} a The first matrix.
+ * @param {mat2d} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];
+}
+
+/**
+ * Returns whether or not the matrices have approximately the same elements in the same position.
+ *
+ * @param {mat2d} a The first matrix.
+ * @param {mat2d} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
+          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
+          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)));
+}
+
+/**
+ * Alias for {@link mat2d.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link mat2d.subtract}
+ * @function
+ */
+export const sub = subtract;
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat3.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat3.js
new file mode 100644
index 0000000..fb6fa8b
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat3.js
@@ -0,0 +1,765 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * 3x3 Matrix
+ * @module mat3
+ */
+
+/**
+ * Creates a new identity mat3
+ *
+ * @returns {mat3} a new 3x3 matrix
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(9);
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 1;
+  out[5] = 0;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 1;
+  return out;
+}
+
+/**
+ * Copies the upper-left 3x3 values into the given mat3.
+ *
+ * @param {mat3} out the receiving 3x3 matrix
+ * @param {mat4} a   the source 4x4 matrix
+ * @returns {mat3} out
+ */
+export function fromMat4(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[4];
+  out[4] = a[5];
+  out[5] = a[6];
+  out[6] = a[8];
+  out[7] = a[9];
+  out[8] = a[10];
+  return out;
+}
+
+/**
+ * Creates a new mat3 initialized with values from an existing matrix
+ *
+ * @param {mat3} a matrix to clone
+ * @returns {mat3} a new 3x3 matrix
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(9);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  out[6] = a[6];
+  out[7] = a[7];
+  out[8] = a[8];
+  return out;
+}
+
+/**
+ * Copy the values from one mat3 to another
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the source matrix
+ * @returns {mat3} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  out[6] = a[6];
+  out[7] = a[7];
+  out[8] = a[8];
+  return out;
+}
+
+/**
+ * Create a new mat3 with the given values
+ *
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m02 Component in column 0, row 2 position (index 2)
+ * @param {Number} m10 Component in column 1, row 0 position (index 3)
+ * @param {Number} m11 Component in column 1, row 1 position (index 4)
+ * @param {Number} m12 Component in column 1, row 2 position (index 5)
+ * @param {Number} m20 Component in column 2, row 0 position (index 6)
+ * @param {Number} m21 Component in column 2, row 1 position (index 7)
+ * @param {Number} m22 Component in column 2, row 2 position (index 8)
+ * @returns {mat3} A new mat3
+ */
+export function fromValues(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
+  let out = new glMatrix.ARRAY_TYPE(9);
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m02;
+  out[3] = m10;
+  out[4] = m11;
+  out[5] = m12;
+  out[6] = m20;
+  out[7] = m21;
+  out[8] = m22;
+  return out;
+}
+
+/**
+ * Set the components of a mat3 to the given values
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m02 Component in column 0, row 2 position (index 2)
+ * @param {Number} m10 Component in column 1, row 0 position (index 3)
+ * @param {Number} m11 Component in column 1, row 1 position (index 4)
+ * @param {Number} m12 Component in column 1, row 2 position (index 5)
+ * @param {Number} m20 Component in column 2, row 0 position (index 6)
+ * @param {Number} m21 Component in column 2, row 1 position (index 7)
+ * @param {Number} m22 Component in column 2, row 2 position (index 8)
+ * @returns {mat3} out
+ */
+export function set(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m02;
+  out[3] = m10;
+  out[4] = m11;
+  out[5] = m12;
+  out[6] = m20;
+  out[7] = m21;
+  out[8] = m22;
+  return out;
+}
+
+/**
+ * Set a mat3 to the identity matrix
+ *
+ * @param {mat3} out the receiving matrix
+ * @returns {mat3} out
+ */
+export function identity(out) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 1;
+  out[5] = 0;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 1;
+  return out;
+}
+
+/**
+ * Transpose the values of a mat3
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the source matrix
+ * @returns {mat3} out
+ */
+export function transpose(out, a) {
+  // If we are transposing ourselves we can skip a few steps but have to cache some values
+  if (out === a) {
+    let a01 = a[1], a02 = a[2], a12 = a[5];
+    out[1] = a[3];
+    out[2] = a[6];
+    out[3] = a01;
+    out[5] = a[7];
+    out[6] = a02;
+    out[7] = a12;
+  } else {
+    out[0] = a[0];
+    out[1] = a[3];
+    out[2] = a[6];
+    out[3] = a[1];
+    out[4] = a[4];
+    out[5] = a[7];
+    out[6] = a[2];
+    out[7] = a[5];
+    out[8] = a[8];
+  }
+
+  return out;
+}
+
+/**
+ * Inverts a mat3
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the source matrix
+ * @returns {mat3} out
+ */
+export function invert(out, a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2];
+  let a10 = a[3], a11 = a[4], a12 = a[5];
+  let a20 = a[6], a21 = a[7], a22 = a[8];
+
+  let b01 = a22 * a11 - a12 * a21;
+  let b11 = -a22 * a10 + a12 * a20;
+  let b21 = a21 * a10 - a11 * a20;
+
+  // Calculate the determinant
+  let det = a00 * b01 + a01 * b11 + a02 * b21;
+
+  if (!det) {
+    return null;
+  }
+  det = 1.0 / det;
+
+  out[0] = b01 * det;
+  out[1] = (-a22 * a01 + a02 * a21) * det;
+  out[2] = (a12 * a01 - a02 * a11) * det;
+  out[3] = b11 * det;
+  out[4] = (a22 * a00 - a02 * a20) * det;
+  out[5] = (-a12 * a00 + a02 * a10) * det;
+  out[6] = b21 * det;
+  out[7] = (-a21 * a00 + a01 * a20) * det;
+  out[8] = (a11 * a00 - a01 * a10) * det;
+  return out;
+}
+
+/**
+ * Calculates the adjugate of a mat3
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the source matrix
+ * @returns {mat3} out
+ */
+export function adjoint(out, a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2];
+  let a10 = a[3], a11 = a[4], a12 = a[5];
+  let a20 = a[6], a21 = a[7], a22 = a[8];
+
+  out[0] = (a11 * a22 - a12 * a21);
+  out[1] = (a02 * a21 - a01 * a22);
+  out[2] = (a01 * a12 - a02 * a11);
+  out[3] = (a12 * a20 - a10 * a22);
+  out[4] = (a00 * a22 - a02 * a20);
+  out[5] = (a02 * a10 - a00 * a12);
+  out[6] = (a10 * a21 - a11 * a20);
+  out[7] = (a01 * a20 - a00 * a21);
+  out[8] = (a00 * a11 - a01 * a10);
+  return out;
+}
+
+/**
+ * Calculates the determinant of a mat3
+ *
+ * @param {mat3} a the source matrix
+ * @returns {Number} determinant of a
+ */
+export function determinant(a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2];
+  let a10 = a[3], a11 = a[4], a12 = a[5];
+  let a20 = a[6], a21 = a[7], a22 = a[8];
+
+  return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
+}
+
+/**
+ * Multiplies two mat3's
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the first operand
+ * @param {mat3} b the second operand
+ * @returns {mat3} out
+ */
+export function multiply(out, a, b) {
+  let a00 = a[0], a01 = a[1], a02 = a[2];
+  let a10 = a[3], a11 = a[4], a12 = a[5];
+  let a20 = a[6], a21 = a[7], a22 = a[8];
+
+  let b00 = b[0], b01 = b[1], b02 = b[2];
+  let b10 = b[3], b11 = b[4], b12 = b[5];
+  let b20 = b[6], b21 = b[7], b22 = b[8];
+
+  out[0] = b00 * a00 + b01 * a10 + b02 * a20;
+  out[1] = b00 * a01 + b01 * a11 + b02 * a21;
+  out[2] = b00 * a02 + b01 * a12 + b02 * a22;
+
+  out[3] = b10 * a00 + b11 * a10 + b12 * a20;
+  out[4] = b10 * a01 + b11 * a11 + b12 * a21;
+  out[5] = b10 * a02 + b11 * a12 + b12 * a22;
+
+  out[6] = b20 * a00 + b21 * a10 + b22 * a20;
+  out[7] = b20 * a01 + b21 * a11 + b22 * a21;
+  out[8] = b20 * a02 + b21 * a12 + b22 * a22;
+  return out;
+}
+
+/**
+ * Translate a mat3 by the given vector
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the matrix to translate
+ * @param {vec2} v vector to translate by
+ * @returns {mat3} out
+ */
+export function translate(out, a, v) {
+  let a00 = a[0], a01 = a[1], a02 = a[2],
+    a10 = a[3], a11 = a[4], a12 = a[5],
+    a20 = a[6], a21 = a[7], a22 = a[8],
+    x = v[0], y = v[1];
+
+  out[0] = a00;
+  out[1] = a01;
+  out[2] = a02;
+
+  out[3] = a10;
+  out[4] = a11;
+  out[5] = a12;
+
+  out[6] = x * a00 + y * a10 + a20;
+  out[7] = x * a01 + y * a11 + a21;
+  out[8] = x * a02 + y * a12 + a22;
+  return out;
+}
+
+/**
+ * Rotates a mat3 by the given angle
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat3} out
+ */
+export function rotate(out, a, rad) {
+  let a00 = a[0], a01 = a[1], a02 = a[2],
+    a10 = a[3], a11 = a[4], a12 = a[5],
+    a20 = a[6], a21 = a[7], a22 = a[8],
+
+    s = Math.sin(rad),
+    c = Math.cos(rad);
+
+  out[0] = c * a00 + s * a10;
+  out[1] = c * a01 + s * a11;
+  out[2] = c * a02 + s * a12;
+
+  out[3] = c * a10 - s * a00;
+  out[4] = c * a11 - s * a01;
+  out[5] = c * a12 - s * a02;
+
+  out[6] = a20;
+  out[7] = a21;
+  out[8] = a22;
+  return out;
+};
+
+/**
+ * Scales the mat3 by the dimensions in the given vec2
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the matrix to rotate
+ * @param {vec2} v the vec2 to scale the matrix by
+ * @returns {mat3} out
+ **/
+export function scale(out, a, v) {
+  let x = v[0], y = v[1];
+
+  out[0] = x * a[0];
+  out[1] = x * a[1];
+  out[2] = x * a[2];
+
+  out[3] = y * a[3];
+  out[4] = y * a[4];
+  out[5] = y * a[5];
+
+  out[6] = a[6];
+  out[7] = a[7];
+  out[8] = a[8];
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector translation
+ * This is equivalent to (but much faster than):
+ *
+ *     mat3.identity(dest);
+ *     mat3.translate(dest, dest, vec);
+ *
+ * @param {mat3} out mat3 receiving operation result
+ * @param {vec2} v Translation vector
+ * @returns {mat3} out
+ */
+export function fromTranslation(out, v) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 1;
+  out[5] = 0;
+  out[6] = v[0];
+  out[7] = v[1];
+  out[8] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a given angle
+ * This is equivalent to (but much faster than):
+ *
+ *     mat3.identity(dest);
+ *     mat3.rotate(dest, dest, rad);
+ *
+ * @param {mat3} out mat3 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat3} out
+ */
+export function fromRotation(out, rad) {
+  let s = Math.sin(rad), c = Math.cos(rad);
+
+  out[0] = c;
+  out[1] = s;
+  out[2] = 0;
+
+  out[3] = -s;
+  out[4] = c;
+  out[5] = 0;
+
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector scaling
+ * This is equivalent to (but much faster than):
+ *
+ *     mat3.identity(dest);
+ *     mat3.scale(dest, dest, vec);
+ *
+ * @param {mat3} out mat3 receiving operation result
+ * @param {vec2} v Scaling vector
+ * @returns {mat3} out
+ */
+export function fromScaling(out, v) {
+  out[0] = v[0];
+  out[1] = 0;
+  out[2] = 0;
+
+  out[3] = 0;
+  out[4] = v[1];
+  out[5] = 0;
+
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 1;
+  return out;
+}
+
+/**
+ * Copies the values from a mat2d into a mat3
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat2d} a the matrix to copy
+ * @returns {mat3} out
+ **/
+export function fromMat2d(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = 0;
+
+  out[3] = a[2];
+  out[4] = a[3];
+  out[5] = 0;
+
+  out[6] = a[4];
+  out[7] = a[5];
+  out[8] = 1;
+  return out;
+}
+
+/**
+* Calculates a 3x3 matrix from the given quaternion
+*
+* @param {mat3} out mat3 receiving operation result
+* @param {quat} q Quaternion to create matrix from
+*
+* @returns {mat3} out
+*/
+export function fromQuat(out, q) {
+  let x = q[0], y = q[1], z = q[2], w = q[3];
+  let x2 = x + x;
+  let y2 = y + y;
+  let z2 = z + z;
+
+  let xx = x * x2;
+  let yx = y * x2;
+  let yy = y * y2;
+  let zx = z * x2;
+  let zy = z * y2;
+  let zz = z * z2;
+  let wx = w * x2;
+  let wy = w * y2;
+  let wz = w * z2;
+
+  out[0] = 1 - yy - zz;
+  out[3] = yx - wz;
+  out[6] = zx + wy;
+
+  out[1] = yx + wz;
+  out[4] = 1 - xx - zz;
+  out[7] = zy - wx;
+
+  out[2] = zx - wy;
+  out[5] = zy + wx;
+  out[8] = 1 - xx - yy;
+
+  return out;
+}
+
+/**
+* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix
+*
+* @param {mat3} out mat3 receiving operation result
+* @param {mat4} a Mat4 to derive the normal matrix from
+*
+* @returns {mat3} out
+*/
+export function normalFromMat4(out, a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+  let b00 = a00 * a11 - a01 * a10;
+  let b01 = a00 * a12 - a02 * a10;
+  let b02 = a00 * a13 - a03 * a10;
+  let b03 = a01 * a12 - a02 * a11;
+  let b04 = a01 * a13 - a03 * a11;
+  let b05 = a02 * a13 - a03 * a12;
+  let b06 = a20 * a31 - a21 * a30;
+  let b07 = a20 * a32 - a22 * a30;
+  let b08 = a20 * a33 - a23 * a30;
+  let b09 = a21 * a32 - a22 * a31;
+  let b10 = a21 * a33 - a23 * a31;
+  let b11 = a22 * a33 - a23 * a32;
+
+  // Calculate the determinant
+  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+  if (!det) {
+    return null;
+  }
+  det = 1.0 / det;
+
+  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+  out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+  out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+
+  out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+  out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+  out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+
+  out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+  out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+  out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+
+  return out;
+}
+
+/**
+ * Generates a 2D projection matrix with the given bounds
+ *
+ * @param {mat3} out mat3 frustum matrix will be written into
+ * @param {number} width Width of your gl context
+ * @param {number} height Height of gl context
+ * @returns {mat3} out
+ */
+export function projection(out, width, height) {
+    out[0] = 2 / width;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = -2 / height;
+    out[5] = 0;
+    out[6] = -1;
+    out[7] = 1;
+    out[8] = 1;
+    return out;
+}
+
+/**
+ * Returns a string representation of a mat3
+ *
+ * @param {mat3} a matrix to represent as a string
+ * @returns {String} string representation of the matrix
+ */
+export function str(a) {
+  return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
+          a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +
+          a[6] + ', ' + a[7] + ', ' + a[8] + ')';
+}
+
+/**
+ * Returns Frobenius norm of a mat3
+ *
+ * @param {mat3} a the matrix to calculate Frobenius norm of
+ * @returns {Number} Frobenius norm
+ */
+export function frob(a) {
+  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2)))
+}
+
+/**
+ * Adds two mat3's
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the first operand
+ * @param {mat3} b the second operand
+ * @returns {mat3} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  out[4] = a[4] + b[4];
+  out[5] = a[5] + b[5];
+  out[6] = a[6] + b[6];
+  out[7] = a[7] + b[7];
+  out[8] = a[8] + b[8];
+  return out;
+}
+
+/**
+ * Subtracts matrix b from matrix a
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the first operand
+ * @param {mat3} b the second operand
+ * @returns {mat3} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  out[3] = a[3] - b[3];
+  out[4] = a[4] - b[4];
+  out[5] = a[5] - b[5];
+  out[6] = a[6] - b[6];
+  out[7] = a[7] - b[7];
+  out[8] = a[8] - b[8];
+  return out;
+}
+
+
+
+/**
+ * Multiply each element of the matrix by a scalar.
+ *
+ * @param {mat3} out the receiving matrix
+ * @param {mat3} a the matrix to scale
+ * @param {Number} b amount to scale the matrix's elements by
+ * @returns {mat3} out
+ */
+export function multiplyScalar(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  out[4] = a[4] * b;
+  out[5] = a[5] * b;
+  out[6] = a[6] * b;
+  out[7] = a[7] * b;
+  out[8] = a[8] * b;
+  return out;
+}
+
+/**
+ * Adds two mat3's after multiplying each element of the second operand by a scalar value.
+ *
+ * @param {mat3} out the receiving vector
+ * @param {mat3} a the first operand
+ * @param {mat3} b the second operand
+ * @param {Number} scale the amount to scale b's elements by before adding
+ * @returns {mat3} out
+ */
+export function multiplyScalarAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  out[3] = a[3] + (b[3] * scale);
+  out[4] = a[4] + (b[4] * scale);
+  out[5] = a[5] + (b[5] * scale);
+  out[6] = a[6] + (b[6] * scale);
+  out[7] = a[7] + (b[7] * scale);
+  out[8] = a[8] + (b[8] * scale);
+  return out;
+}
+
+/**
+ * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {mat3} a The first matrix.
+ * @param {mat3} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] &&
+         a[3] === b[3] && a[4] === b[4] && a[5] === b[5] &&
+         a[6] === b[6] && a[7] === b[7] && a[8] === b[8];
+}
+
+/**
+ * Returns whether or not the matrices have approximately the same elements in the same position.
+ *
+ * @param {mat3} a The first matrix.
+ * @param {mat3} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7], a8 = a[8];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
+          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
+          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
+          Math.abs(a6 - b6) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
+          Math.abs(a7 - b7) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
+          Math.abs(a8 - b8) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a8), Math.abs(b8)));
+}
+
+/**
+ * Alias for {@link mat3.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link mat3.subtract}
+ * @function
+ */
+export const sub = subtract;
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat4.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat4.js
new file mode 100644
index 0000000..451e7c4
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/mat4.js
@@ -0,0 +1,1727 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * @class 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
+ * @name mat4
+ */
+
+/**
+ * Creates a new identity mat4
+ *
+ * @returns {mat4} a new 4x4 matrix
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(16);
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = 1;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = 1;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a new mat4 initialized with values from an existing matrix
+ *
+ * @param {mat4} a matrix to clone
+ * @returns {mat4} a new 4x4 matrix
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(16);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  out[6] = a[6];
+  out[7] = a[7];
+  out[8] = a[8];
+  out[9] = a[9];
+  out[10] = a[10];
+  out[11] = a[11];
+  out[12] = a[12];
+  out[13] = a[13];
+  out[14] = a[14];
+  out[15] = a[15];
+  return out;
+}
+
+/**
+ * Copy the values from one mat4 to another
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the source matrix
+ * @returns {mat4} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  out[6] = a[6];
+  out[7] = a[7];
+  out[8] = a[8];
+  out[9] = a[9];
+  out[10] = a[10];
+  out[11] = a[11];
+  out[12] = a[12];
+  out[13] = a[13];
+  out[14] = a[14];
+  out[15] = a[15];
+  return out;
+}
+
+/**
+ * Create a new mat4 with the given values
+ *
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m02 Component in column 0, row 2 position (index 2)
+ * @param {Number} m03 Component in column 0, row 3 position (index 3)
+ * @param {Number} m10 Component in column 1, row 0 position (index 4)
+ * @param {Number} m11 Component in column 1, row 1 position (index 5)
+ * @param {Number} m12 Component in column 1, row 2 position (index 6)
+ * @param {Number} m13 Component in column 1, row 3 position (index 7)
+ * @param {Number} m20 Component in column 2, row 0 position (index 8)
+ * @param {Number} m21 Component in column 2, row 1 position (index 9)
+ * @param {Number} m22 Component in column 2, row 2 position (index 10)
+ * @param {Number} m23 Component in column 2, row 3 position (index 11)
+ * @param {Number} m30 Component in column 3, row 0 position (index 12)
+ * @param {Number} m31 Component in column 3, row 1 position (index 13)
+ * @param {Number} m32 Component in column 3, row 2 position (index 14)
+ * @param {Number} m33 Component in column 3, row 3 position (index 15)
+ * @returns {mat4} A new mat4
+ */
+export function fromValues(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
+  let out = new glMatrix.ARRAY_TYPE(16);
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m02;
+  out[3] = m03;
+  out[4] = m10;
+  out[5] = m11;
+  out[6] = m12;
+  out[7] = m13;
+  out[8] = m20;
+  out[9] = m21;
+  out[10] = m22;
+  out[11] = m23;
+  out[12] = m30;
+  out[13] = m31;
+  out[14] = m32;
+  out[15] = m33;
+  return out;
+}
+
+/**
+ * Set the components of a mat4 to the given values
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {Number} m00 Component in column 0, row 0 position (index 0)
+ * @param {Number} m01 Component in column 0, row 1 position (index 1)
+ * @param {Number} m02 Component in column 0, row 2 position (index 2)
+ * @param {Number} m03 Component in column 0, row 3 position (index 3)
+ * @param {Number} m10 Component in column 1, row 0 position (index 4)
+ * @param {Number} m11 Component in column 1, row 1 position (index 5)
+ * @param {Number} m12 Component in column 1, row 2 position (index 6)
+ * @param {Number} m13 Component in column 1, row 3 position (index 7)
+ * @param {Number} m20 Component in column 2, row 0 position (index 8)
+ * @param {Number} m21 Component in column 2, row 1 position (index 9)
+ * @param {Number} m22 Component in column 2, row 2 position (index 10)
+ * @param {Number} m23 Component in column 2, row 3 position (index 11)
+ * @param {Number} m30 Component in column 3, row 0 position (index 12)
+ * @param {Number} m31 Component in column 3, row 1 position (index 13)
+ * @param {Number} m32 Component in column 3, row 2 position (index 14)
+ * @param {Number} m33 Component in column 3, row 3 position (index 15)
+ * @returns {mat4} out
+ */
+export function set(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
+  out[0] = m00;
+  out[1] = m01;
+  out[2] = m02;
+  out[3] = m03;
+  out[4] = m10;
+  out[5] = m11;
+  out[6] = m12;
+  out[7] = m13;
+  out[8] = m20;
+  out[9] = m21;
+  out[10] = m22;
+  out[11] = m23;
+  out[12] = m30;
+  out[13] = m31;
+  out[14] = m32;
+  out[15] = m33;
+  return out;
+}
+
+
+/**
+ * Set a mat4 to the identity matrix
+ *
+ * @param {mat4} out the receiving matrix
+ * @returns {mat4} out
+ */
+export function identity(out) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = 1;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = 1;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Transpose the values of a mat4
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the source matrix
+ * @returns {mat4} out
+ */
+export function transpose(out, a) {
+  // If we are transposing ourselves we can skip a few steps but have to cache some values
+  if (out === a) {
+    let a01 = a[1], a02 = a[2], a03 = a[3];
+    let a12 = a[6], a13 = a[7];
+    let a23 = a[11];
+
+    out[1] = a[4];
+    out[2] = a[8];
+    out[3] = a[12];
+    out[4] = a01;
+    out[6] = a[9];
+    out[7] = a[13];
+    out[8] = a02;
+    out[9] = a12;
+    out[11] = a[14];
+    out[12] = a03;
+    out[13] = a13;
+    out[14] = a23;
+  } else {
+    out[0] = a[0];
+    out[1] = a[4];
+    out[2] = a[8];
+    out[3] = a[12];
+    out[4] = a[1];
+    out[5] = a[5];
+    out[6] = a[9];
+    out[7] = a[13];
+    out[8] = a[2];
+    out[9] = a[6];
+    out[10] = a[10];
+    out[11] = a[14];
+    out[12] = a[3];
+    out[13] = a[7];
+    out[14] = a[11];
+    out[15] = a[15];
+  }
+
+  return out;
+}
+
+/**
+ * Inverts a mat4
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the source matrix
+ * @returns {mat4} out
+ */
+export function invert(out, a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+  let b00 = a00 * a11 - a01 * a10;
+  let b01 = a00 * a12 - a02 * a10;
+  let b02 = a00 * a13 - a03 * a10;
+  let b03 = a01 * a12 - a02 * a11;
+  let b04 = a01 * a13 - a03 * a11;
+  let b05 = a02 * a13 - a03 * a12;
+  let b06 = a20 * a31 - a21 * a30;
+  let b07 = a20 * a32 - a22 * a30;
+  let b08 = a20 * a33 - a23 * a30;
+  let b09 = a21 * a32 - a22 * a31;
+  let b10 = a21 * a33 - a23 * a31;
+  let b11 = a22 * a33 - a23 * a32;
+
+  // Calculate the determinant
+  let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+  if (!det) {
+    return null;
+  }
+  det = 1.0 / det;
+
+  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+  out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+  out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+  out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+  out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+  out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+  out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+  out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+  out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+  out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+  out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+  out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+  out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+  out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+  out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+  out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+
+  return out;
+}
+
+/**
+ * Calculates the adjugate of a mat4
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the source matrix
+ * @returns {mat4} out
+ */
+export function adjoint(out, a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+  out[0]  =  (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
+  out[1]  = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
+  out[2]  =  (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
+  out[3]  = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
+  out[4]  = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
+  out[5]  =  (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
+  out[6]  = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
+  out[7]  =  (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
+  out[8]  =  (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
+  out[9]  = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
+  out[10] =  (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
+  out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
+  out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
+  out[13] =  (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
+  out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
+  out[15] =  (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
+  return out;
+}
+
+/**
+ * Calculates the determinant of a mat4
+ *
+ * @param {mat4} a the source matrix
+ * @returns {Number} determinant of a
+ */
+export function determinant(a) {
+  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+  let b00 = a00 * a11 - a01 * a10;
+  let b01 = a00 * a12 - a02 * a10;
+  let b02 = a00 * a13 - a03 * a10;
+  let b03 = a01 * a12 - a02 * a11;
+  let b04 = a01 * a13 - a03 * a11;
+  let b05 = a02 * a13 - a03 * a12;
+  let b06 = a20 * a31 - a21 * a30;
+  let b07 = a20 * a32 - a22 * a30;
+  let b08 = a20 * a33 - a23 * a30;
+  let b09 = a21 * a32 - a22 * a31;
+  let b10 = a21 * a33 - a23 * a31;
+  let b11 = a22 * a33 - a23 * a32;
+
+  // Calculate the determinant
+  return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+}
+
+/**
+ * Multiplies two mat4s
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the first operand
+ * @param {mat4} b the second operand
+ * @returns {mat4} out
+ */
+export function multiply(out, a, b) {
+  let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
+  let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
+  let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
+  let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
+
+  // Cache only the current line of the second matrix
+  let b0  = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
+  out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
+  out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
+  out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
+  out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
+
+  b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
+  out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
+  out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
+  out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
+  out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
+
+  b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
+  out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
+  out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
+  out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
+  out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
+
+  b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
+  out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
+  out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
+  out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
+  out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
+  return out;
+}
+
+/**
+ * Translate a mat4 by the given vector
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to translate
+ * @param {vec3} v vector to translate by
+ * @returns {mat4} out
+ */
+export function translate(out, a, v) {
+  let x = v[0], y = v[1], z = v[2];
+  let a00, a01, a02, a03;
+  let a10, a11, a12, a13;
+  let a20, a21, a22, a23;
+
+  if (a === out) {
+    out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
+    out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
+    out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
+    out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
+  } else {
+    a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
+    a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
+    a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
+
+    out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
+    out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
+    out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
+
+    out[12] = a00 * x + a10 * y + a20 * z + a[12];
+    out[13] = a01 * x + a11 * y + a21 * z + a[13];
+    out[14] = a02 * x + a12 * y + a22 * z + a[14];
+    out[15] = a03 * x + a13 * y + a23 * z + a[15];
+  }
+
+  return out;
+}
+
+/**
+ * Scales the mat4 by the dimensions in the given vec3 not using vectorization
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to scale
+ * @param {vec3} v the vec3 to scale the matrix by
+ * @returns {mat4} out
+ **/
+export function scale(out, a, v) {
+  let x = v[0], y = v[1], z = v[2];
+
+  out[0] = a[0] * x;
+  out[1] = a[1] * x;
+  out[2] = a[2] * x;
+  out[3] = a[3] * x;
+  out[4] = a[4] * y;
+  out[5] = a[5] * y;
+  out[6] = a[6] * y;
+  out[7] = a[7] * y;
+  out[8] = a[8] * z;
+  out[9] = a[9] * z;
+  out[10] = a[10] * z;
+  out[11] = a[11] * z;
+  out[12] = a[12];
+  out[13] = a[13];
+  out[14] = a[14];
+  out[15] = a[15];
+  return out;
+}
+
+/**
+ * Rotates a mat4 by the given angle around the given axis
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @param {vec3} axis the axis to rotate around
+ * @returns {mat4} out
+ */
+export function rotate(out, a, rad, axis) {
+  let x = axis[0], y = axis[1], z = axis[2];
+  let len = Math.sqrt(x * x + y * y + z * z);
+  let s, c, t;
+  let a00, a01, a02, a03;
+  let a10, a11, a12, a13;
+  let a20, a21, a22, a23;
+  let b00, b01, b02;
+  let b10, b11, b12;
+  let b20, b21, b22;
+
+  if (Math.abs(len) < glMatrix.EPSILON) { return null; }
+
+  len = 1 / len;
+  x *= len;
+  y *= len;
+  z *= len;
+
+  s = Math.sin(rad);
+  c = Math.cos(rad);
+  t = 1 - c;
+
+  a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
+  a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
+  a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
+
+  // Construct the elements of the rotation matrix
+  b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
+  b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
+  b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
+
+  // Perform rotation-specific matrix multiplication
+  out[0] = a00 * b00 + a10 * b01 + a20 * b02;
+  out[1] = a01 * b00 + a11 * b01 + a21 * b02;
+  out[2] = a02 * b00 + a12 * b01 + a22 * b02;
+  out[3] = a03 * b00 + a13 * b01 + a23 * b02;
+  out[4] = a00 * b10 + a10 * b11 + a20 * b12;
+  out[5] = a01 * b10 + a11 * b11 + a21 * b12;
+  out[6] = a02 * b10 + a12 * b11 + a22 * b12;
+  out[7] = a03 * b10 + a13 * b11 + a23 * b12;
+  out[8] = a00 * b20 + a10 * b21 + a20 * b22;
+  out[9] = a01 * b20 + a11 * b21 + a21 * b22;
+  out[10] = a02 * b20 + a12 * b21 + a22 * b22;
+  out[11] = a03 * b20 + a13 * b21 + a23 * b22;
+
+  if (a !== out) { // If the source and destination differ, copy the unchanged last row
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+  }
+  return out;
+}
+
+/**
+ * Rotates a matrix by the given angle around the X axis
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function rotateX(out, a, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  let a10 = a[4];
+  let a11 = a[5];
+  let a12 = a[6];
+  let a13 = a[7];
+  let a20 = a[8];
+  let a21 = a[9];
+  let a22 = a[10];
+  let a23 = a[11];
+
+  if (a !== out) { // If the source and destination differ, copy the unchanged rows
+    out[0]  = a[0];
+    out[1]  = a[1];
+    out[2]  = a[2];
+    out[3]  = a[3];
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+  }
+
+  // Perform axis-specific matrix multiplication
+  out[4] = a10 * c + a20 * s;
+  out[5] = a11 * c + a21 * s;
+  out[6] = a12 * c + a22 * s;
+  out[7] = a13 * c + a23 * s;
+  out[8] = a20 * c - a10 * s;
+  out[9] = a21 * c - a11 * s;
+  out[10] = a22 * c - a12 * s;
+  out[11] = a23 * c - a13 * s;
+  return out;
+}
+
+/**
+ * Rotates a matrix by the given angle around the Y axis
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function rotateY(out, a, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  let a00 = a[0];
+  let a01 = a[1];
+  let a02 = a[2];
+  let a03 = a[3];
+  let a20 = a[8];
+  let a21 = a[9];
+  let a22 = a[10];
+  let a23 = a[11];
+
+  if (a !== out) { // If the source and destination differ, copy the unchanged rows
+    out[4]  = a[4];
+    out[5]  = a[5];
+    out[6]  = a[6];
+    out[7]  = a[7];
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+  }
+
+  // Perform axis-specific matrix multiplication
+  out[0] = a00 * c - a20 * s;
+  out[1] = a01 * c - a21 * s;
+  out[2] = a02 * c - a22 * s;
+  out[3] = a03 * c - a23 * s;
+  out[8] = a00 * s + a20 * c;
+  out[9] = a01 * s + a21 * c;
+  out[10] = a02 * s + a22 * c;
+  out[11] = a03 * s + a23 * c;
+  return out;
+}
+
+/**
+ * Rotates a matrix by the given angle around the Z axis
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to rotate
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function rotateZ(out, a, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+  let a00 = a[0];
+  let a01 = a[1];
+  let a02 = a[2];
+  let a03 = a[3];
+  let a10 = a[4];
+  let a11 = a[5];
+  let a12 = a[6];
+  let a13 = a[7];
+
+  if (a !== out) { // If the source and destination differ, copy the unchanged last row
+    out[8]  = a[8];
+    out[9]  = a[9];
+    out[10] = a[10];
+    out[11] = a[11];
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+  }
+
+  // Perform axis-specific matrix multiplication
+  out[0] = a00 * c + a10 * s;
+  out[1] = a01 * c + a11 * s;
+  out[2] = a02 * c + a12 * s;
+  out[3] = a03 * c + a13 * s;
+  out[4] = a10 * c - a00 * s;
+  out[5] = a11 * c - a01 * s;
+  out[6] = a12 * c - a02 * s;
+  out[7] = a13 * c - a03 * s;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector translation
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.translate(dest, dest, vec);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {vec3} v Translation vector
+ * @returns {mat4} out
+ */
+export function fromTranslation(out, v) {
+  out[0] = 1;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = 1;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = 1;
+  out[11] = 0;
+  out[12] = v[0];
+  out[13] = v[1];
+  out[14] = v[2];
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a vector scaling
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.scale(dest, dest, vec);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {vec3} v Scaling vector
+ * @returns {mat4} out
+ */
+export function fromScaling(out, v) {
+  out[0] = v[0];
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = v[1];
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = v[2];
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a given angle around a given axis
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.rotate(dest, dest, rad, axis);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @param {vec3} axis the axis to rotate around
+ * @returns {mat4} out
+ */
+export function fromRotation(out, rad, axis) {
+  let x = axis[0], y = axis[1], z = axis[2];
+  let len = Math.sqrt(x * x + y * y + z * z);
+  let s, c, t;
+
+  if (Math.abs(len) < glMatrix.EPSILON) { return null; }
+
+  len = 1 / len;
+  x *= len;
+  y *= len;
+  z *= len;
+
+  s = Math.sin(rad);
+  c = Math.cos(rad);
+  t = 1 - c;
+
+  // Perform rotation-specific matrix multiplication
+  out[0] = x * x * t + c;
+  out[1] = y * x * t + z * s;
+  out[2] = z * x * t - y * s;
+  out[3] = 0;
+  out[4] = x * y * t - z * s;
+  out[5] = y * y * t + c;
+  out[6] = z * y * t + x * s;
+  out[7] = 0;
+  out[8] = x * z * t + y * s;
+  out[9] = y * z * t - x * s;
+  out[10] = z * z * t + c;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from the given angle around the X axis
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.rotateX(dest, dest, rad);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function fromXRotation(out, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+
+  // Perform axis-specific matrix multiplication
+  out[0]  = 1;
+  out[1]  = 0;
+  out[2]  = 0;
+  out[3]  = 0;
+  out[4] = 0;
+  out[5] = c;
+  out[6] = s;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = -s;
+  out[10] = c;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from the given angle around the Y axis
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.rotateY(dest, dest, rad);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function fromYRotation(out, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+
+  // Perform axis-specific matrix multiplication
+  out[0]  = c;
+  out[1]  = 0;
+  out[2]  = -s;
+  out[3]  = 0;
+  out[4] = 0;
+  out[5] = 1;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = s;
+  out[9] = 0;
+  out[10] = c;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from the given angle around the Z axis
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.rotateZ(dest, dest, rad);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {Number} rad the angle to rotate the matrix by
+ * @returns {mat4} out
+ */
+export function fromZRotation(out, rad) {
+  let s = Math.sin(rad);
+  let c = Math.cos(rad);
+
+  // Perform axis-specific matrix multiplication
+  out[0]  = c;
+  out[1]  = s;
+  out[2]  = 0;
+  out[3]  = 0;
+  out[4] = -s;
+  out[5] = c;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = 1;
+  out[11] = 0;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Creates a matrix from a quaternion rotation and vector translation
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.translate(dest, vec);
+ *     let quatMat = mat4.create();
+ *     quat4.toMat4(quat, quatMat);
+ *     mat4.multiply(dest, quatMat);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {quat4} q Rotation quaternion
+ * @param {vec3} v Translation vector
+ * @returns {mat4} out
+ */
+export function fromRotationTranslation(out, q, v) {
+  // Quaternion math
+  let x = q[0], y = q[1], z = q[2], w = q[3];
+  let x2 = x + x;
+  let y2 = y + y;
+  let z2 = z + z;
+
+  let xx = x * x2;
+  let xy = x * y2;
+  let xz = x * z2;
+  let yy = y * y2;
+  let yz = y * z2;
+  let zz = z * z2;
+  let wx = w * x2;
+  let wy = w * y2;
+  let wz = w * z2;
+
+  out[0] = 1 - (yy + zz);
+  out[1] = xy + wz;
+  out[2] = xz - wy;
+  out[3] = 0;
+  out[4] = xy - wz;
+  out[5] = 1 - (xx + zz);
+  out[6] = yz + wx;
+  out[7] = 0;
+  out[8] = xz + wy;
+  out[9] = yz - wx;
+  out[10] = 1 - (xx + yy);
+  out[11] = 0;
+  out[12] = v[0];
+  out[13] = v[1];
+  out[14] = v[2];
+  out[15] = 1;
+
+  return out;
+}
+
+/**
+ * Creates a new mat4 from a dual quat.
+ *
+ * @param {mat4} out Matrix
+ * @param {quat2} a Dual Quaternion
+ * @returns {mat4} mat4 receiving operation result
+ */
+export function fromQuat2(out, a) {
+  let translation = new glMatrix.ARRAY_TYPE(3);
+  let bx = -a[0], by = -a[1], bz = -a[2], bw = a[3],
+  ax = a[4], ay = a[5], az = a[6], aw = a[7];
+  
+  let magnitude = bx * bx + by * by + bz * bz + bw * bw;
+  //Only scale if it makes sense
+  if (magnitude > 0) {
+    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
+    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
+    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
+  } else {
+    translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
+    translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
+    translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
+  }
+  fromRotationTranslation(out, a, translation);
+  return out;
+}
+
+/**
+ * Returns the translation vector component of a transformation
+ *  matrix. If a matrix is built with fromRotationTranslation,
+ *  the returned vector will be the same as the translation vector
+ *  originally supplied.
+ * @param  {vec3} out Vector to receive translation component
+ * @param  {mat4} mat Matrix to be decomposed (input)
+ * @return {vec3} out
+ */
+export function getTranslation(out, mat) {
+  out[0] = mat[12];
+  out[1] = mat[13];
+  out[2] = mat[14];
+
+  return out;
+}
+
+/**
+ * Returns the scaling factor component of a transformation
+ *  matrix. If a matrix is built with fromRotationTranslationScale
+ *  with a normalized Quaternion paramter, the returned vector will be
+ *  the same as the scaling vector
+ *  originally supplied.
+ * @param  {vec3} out Vector to receive scaling factor component
+ * @param  {mat4} mat Matrix to be decomposed (input)
+ * @return {vec3} out
+ */
+export function getScaling(out, mat) {
+  let m11 = mat[0];
+  let m12 = mat[1];
+  let m13 = mat[2];
+  let m21 = mat[4];
+  let m22 = mat[5];
+  let m23 = mat[6];
+  let m31 = mat[8];
+  let m32 = mat[9];
+  let m33 = mat[10];
+
+  out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
+  out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
+  out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
+
+  return out;
+}
+
+/**
+ * Returns a quaternion representing the rotational component
+ *  of a transformation matrix. If a matrix is built with
+ *  fromRotationTranslation, the returned quaternion will be the
+ *  same as the quaternion originally supplied.
+ * @param {quat} out Quaternion to receive the rotation component
+ * @param {mat4} mat Matrix to be decomposed (input)
+ * @return {quat} out
+ */
+export function getRotation(out, mat) {
+  // Algorithm taken from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+  let trace = mat[0] + mat[5] + mat[10];
+  let S = 0;
+
+  if (trace > 0) {
+    S = Math.sqrt(trace + 1.0) * 2;
+    out[3] = 0.25 * S;
+    out[0] = (mat[6] - mat[9]) / S;
+    out[1] = (mat[8] - mat[2]) / S;
+    out[2] = (mat[1] - mat[4]) / S;
+  } else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) {
+    S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2;
+    out[3] = (mat[6] - mat[9]) / S;
+    out[0] = 0.25 * S;
+    out[1] = (mat[1] + mat[4]) / S;
+    out[2] = (mat[8] + mat[2]) / S;
+  } else if (mat[5] > mat[10]) {
+    S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2;
+    out[3] = (mat[8] - mat[2]) / S;
+    out[0] = (mat[1] + mat[4]) / S;
+    out[1] = 0.25 * S;
+    out[2] = (mat[6] + mat[9]) / S;
+  } else {
+    S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2;
+    out[3] = (mat[1] - mat[4]) / S;
+    out[0] = (mat[8] + mat[2]) / S;
+    out[1] = (mat[6] + mat[9]) / S;
+    out[2] = 0.25 * S;
+  }
+
+  return out;
+}
+
+/**
+ * Creates a matrix from a quaternion rotation, vector translation and vector scale
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.translate(dest, vec);
+ *     let quatMat = mat4.create();
+ *     quat4.toMat4(quat, quatMat);
+ *     mat4.multiply(dest, quatMat);
+ *     mat4.scale(dest, scale)
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {quat4} q Rotation quaternion
+ * @param {vec3} v Translation vector
+ * @param {vec3} s Scaling vector
+ * @returns {mat4} out
+ */
+export function fromRotationTranslationScale(out, q, v, s) {
+  // Quaternion math
+  let x = q[0], y = q[1], z = q[2], w = q[3];
+  let x2 = x + x;
+  let y2 = y + y;
+  let z2 = z + z;
+
+  let xx = x * x2;
+  let xy = x * y2;
+  let xz = x * z2;
+  let yy = y * y2;
+  let yz = y * z2;
+  let zz = z * z2;
+  let wx = w * x2;
+  let wy = w * y2;
+  let wz = w * z2;
+  let sx = s[0];
+  let sy = s[1];
+  let sz = s[2];
+
+  out[0] = (1 - (yy + zz)) * sx;
+  out[1] = (xy + wz) * sx;
+  out[2] = (xz - wy) * sx;
+  out[3] = 0;
+  out[4] = (xy - wz) * sy;
+  out[5] = (1 - (xx + zz)) * sy;
+  out[6] = (yz + wx) * sy;
+  out[7] = 0;
+  out[8] = (xz + wy) * sz;
+  out[9] = (yz - wx) * sz;
+  out[10] = (1 - (xx + yy)) * sz;
+  out[11] = 0;
+  out[12] = v[0];
+  out[13] = v[1];
+  out[14] = v[2];
+  out[15] = 1;
+
+  return out;
+}
+
+/**
+ * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin
+ * This is equivalent to (but much faster than):
+ *
+ *     mat4.identity(dest);
+ *     mat4.translate(dest, vec);
+ *     mat4.translate(dest, origin);
+ *     let quatMat = mat4.create();
+ *     quat4.toMat4(quat, quatMat);
+ *     mat4.multiply(dest, quatMat);
+ *     mat4.scale(dest, scale)
+ *     mat4.translate(dest, negativeOrigin);
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {quat4} q Rotation quaternion
+ * @param {vec3} v Translation vector
+ * @param {vec3} s Scaling vector
+ * @param {vec3} o The origin vector around which to scale and rotate
+ * @returns {mat4} out
+ */
+export function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
+  // Quaternion math
+  let x = q[0], y = q[1], z = q[2], w = q[3];
+  let x2 = x + x;
+  let y2 = y + y;
+  let z2 = z + z;
+
+  let xx = x * x2;
+  let xy = x * y2;
+  let xz = x * z2;
+  let yy = y * y2;
+  let yz = y * z2;
+  let zz = z * z2;
+  let wx = w * x2;
+  let wy = w * y2;
+  let wz = w * z2;
+
+  let sx = s[0];
+  let sy = s[1];
+  let sz = s[2];
+
+  let ox = o[0];
+  let oy = o[1];
+  let oz = o[2];
+
+  let out0 = (1 - (yy + zz)) * sx;
+  let out1 = (xy + wz) * sx;
+  let out2 = (xz - wy) * sx;
+  let out4 = (xy - wz) * sy;
+  let out5 = (1 - (xx + zz)) * sy;
+  let out6 = (yz + wx) * sy;
+  let out8 = (xz + wy) * sz;
+  let out9 = (yz - wx) * sz;
+  let out10 = (1 - (xx + yy)) * sz;
+
+  out[0] = out0;
+  out[1] = out1;
+  out[2] = out2;
+  out[3] = 0;
+  out[4] = out4;
+  out[5] = out5;
+  out[6] = out6;
+  out[7] = 0;
+  out[8] = out8;
+  out[9] = out9;
+  out[10] = out10;
+  out[11] = 0;
+  out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
+  out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
+  out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
+  out[15] = 1;
+
+  return out;
+}
+
+/**
+ * Calculates a 4x4 matrix from the given quaternion
+ *
+ * @param {mat4} out mat4 receiving operation result
+ * @param {quat} q Quaternion to create matrix from
+ *
+ * @returns {mat4} out
+ */
+export function fromQuat(out, q) {
+  let x = q[0], y = q[1], z = q[2], w = q[3];
+  let x2 = x + x;
+  let y2 = y + y;
+  let z2 = z + z;
+
+  let xx = x * x2;
+  let yx = y * x2;
+  let yy = y * y2;
+  let zx = z * x2;
+  let zy = z * y2;
+  let zz = z * z2;
+  let wx = w * x2;
+  let wy = w * y2;
+  let wz = w * z2;
+
+  out[0] = 1 - yy - zz;
+  out[1] = yx + wz;
+  out[2] = zx - wy;
+  out[3] = 0;
+
+  out[4] = yx - wz;
+  out[5] = 1 - xx - zz;
+  out[6] = zy + wx;
+  out[7] = 0;
+
+  out[8] = zx + wy;
+  out[9] = zy - wx;
+  out[10] = 1 - xx - yy;
+  out[11] = 0;
+
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = 0;
+  out[15] = 1;
+
+  return out;
+}
+
+/**
+ * Generates a frustum matrix with the given bounds
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {Number} left Left bound of the frustum
+ * @param {Number} right Right bound of the frustum
+ * @param {Number} bottom Bottom bound of the frustum
+ * @param {Number} top Top bound of the frustum
+ * @param {Number} near Near bound of the frustum
+ * @param {Number} far Far bound of the frustum
+ * @returns {mat4} out
+ */
+export function frustum(out, left, right, bottom, top, near, far) {
+  let rl = 1 / (right - left);
+  let tb = 1 / (top - bottom);
+  let nf = 1 / (near - far);
+  out[0] = (near * 2) * rl;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = (near * 2) * tb;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = (right + left) * rl;
+  out[9] = (top + bottom) * tb;
+  out[10] = (far + near) * nf;
+  out[11] = -1;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = (far * near * 2) * nf;
+  out[15] = 0;
+  return out;
+}
+
+/**
+ * Generates a perspective projection matrix with the given bounds
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {number} fovy Vertical field of view in radians
+ * @param {number} aspect Aspect ratio. typically viewport width/height
+ * @param {number} near Near bound of the frustum
+ * @param {number} far Far bound of the frustum
+ * @returns {mat4} out
+ */
+export function perspective(out, fovy, aspect, near, far) {
+  let f = 1.0 / Math.tan(fovy / 2);
+  let nf = 1 / (near - far);
+  out[0] = f / aspect;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = f;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = (far + near) * nf;
+  out[11] = -1;
+  out[12] = 0;
+  out[13] = 0;
+  out[14] = (2 * far * near) * nf;
+  out[15] = 0;
+  return out;
+}
+
+/**
+ * Generates a perspective projection matrix with the given field of view.
+ * This is primarily useful for generating projection matrices to be used
+ * with the still experiemental WebVR API.
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
+ * @param {number} near Near bound of the frustum
+ * @param {number} far Far bound of the frustum
+ * @returns {mat4} out
+ */
+export function perspectiveFromFieldOfView(out, fov, near, far) {
+  let upTan = Math.tan(fov.upDegrees * Math.PI/180.0);
+  let downTan = Math.tan(fov.downDegrees * Math.PI/180.0);
+  let leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0);
+  let rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0);
+  let xScale = 2.0 / (leftTan + rightTan);
+  let yScale = 2.0 / (upTan + downTan);
+
+  out[0] = xScale;
+  out[1] = 0.0;
+  out[2] = 0.0;
+  out[3] = 0.0;
+  out[4] = 0.0;
+  out[5] = yScale;
+  out[6] = 0.0;
+  out[7] = 0.0;
+  out[8] = -((leftTan - rightTan) * xScale * 0.5);
+  out[9] = ((upTan - downTan) * yScale * 0.5);
+  out[10] = far / (near - far);
+  out[11] = -1.0;
+  out[12] = 0.0;
+  out[13] = 0.0;
+  out[14] = (far * near) / (near - far);
+  out[15] = 0.0;
+  return out;
+}
+
+/**
+ * Generates a orthogonal projection matrix with the given bounds
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {number} left Left bound of the frustum
+ * @param {number} right Right bound of the frustum
+ * @param {number} bottom Bottom bound of the frustum
+ * @param {number} top Top bound of the frustum
+ * @param {number} near Near bound of the frustum
+ * @param {number} far Far bound of the frustum
+ * @returns {mat4} out
+ */
+export function ortho(out, left, right, bottom, top, near, far) {
+  let lr = 1 / (left - right);
+  let bt = 1 / (bottom - top);
+  let nf = 1 / (near - far);
+  out[0] = -2 * lr;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  out[4] = 0;
+  out[5] = -2 * bt;
+  out[6] = 0;
+  out[7] = 0;
+  out[8] = 0;
+  out[9] = 0;
+  out[10] = 2 * nf;
+  out[11] = 0;
+  out[12] = (left + right) * lr;
+  out[13] = (top + bottom) * bt;
+  out[14] = (far + near) * nf;
+  out[15] = 1;
+  return out;
+}
+
+/**
+ * Generates a look-at matrix with the given eye position, focal point, and up axis. 
+ * If you want a matrix that actually makes an object look at another object, you should use targetTo instead.
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {vec3} eye Position of the viewer
+ * @param {vec3} center Point the viewer is looking at
+ * @param {vec3} up vec3 pointing up
+ * @returns {mat4} out
+ */
+export function lookAt(out, eye, center, up) {
+  let x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
+  let eyex = eye[0];
+  let eyey = eye[1];
+  let eyez = eye[2];
+  let upx = up[0];
+  let upy = up[1];
+  let upz = up[2];
+  let centerx = center[0];
+  let centery = center[1];
+  let centerz = center[2];
+
+  if (Math.abs(eyex - centerx) < glMatrix.EPSILON &&
+      Math.abs(eyey - centery) < glMatrix.EPSILON &&
+      Math.abs(eyez - centerz) < glMatrix.EPSILON) {
+    return identity(out);
+  }
+
+  z0 = eyex - centerx;
+  z1 = eyey - centery;
+  z2 = eyez - centerz;
+
+  len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
+  z0 *= len;
+  z1 *= len;
+  z2 *= len;
+
+  x0 = upy * z2 - upz * z1;
+  x1 = upz * z0 - upx * z2;
+  x2 = upx * z1 - upy * z0;
+  len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+  if (!len) {
+    x0 = 0;
+    x1 = 0;
+    x2 = 0;
+  } else {
+    len = 1 / len;
+    x0 *= len;
+    x1 *= len;
+    x2 *= len;
+  }
+
+  y0 = z1 * x2 - z2 * x1;
+  y1 = z2 * x0 - z0 * x2;
+  y2 = z0 * x1 - z1 * x0;
+
+  len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
+  if (!len) {
+    y0 = 0;
+    y1 = 0;
+    y2 = 0;
+  } else {
+    len = 1 / len;
+    y0 *= len;
+    y1 *= len;
+    y2 *= len;
+  }
+
+  out[0] = x0;
+  out[1] = y0;
+  out[2] = z0;
+  out[3] = 0;
+  out[4] = x1;
+  out[5] = y1;
+  out[6] = z1;
+  out[7] = 0;
+  out[8] = x2;
+  out[9] = y2;
+  out[10] = z2;
+  out[11] = 0;
+  out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
+  out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
+  out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
+  out[15] = 1;
+
+  return out;
+}
+
+/**
+ * Generates a matrix that makes something look at something else.
+ *
+ * @param {mat4} out mat4 frustum matrix will be written into
+ * @param {vec3} eye Position of the viewer
+ * @param {vec3} center Point the viewer is looking at
+ * @param {vec3} up vec3 pointing up
+ * @returns {mat4} out
+ */
+export function targetTo(out, eye, target, up) {
+  let eyex = eye[0],
+      eyey = eye[1],
+      eyez = eye[2],
+      upx = up[0],
+      upy = up[1],
+      upz = up[2];
+
+  let z0 = eyex - target[0],
+      z1 = eyey - target[1],
+      z2 = eyez - target[2];
+
+  let len = z0*z0 + z1*z1 + z2*z2;
+  if (len > 0) {
+    len = 1 / Math.sqrt(len);
+    z0 *= len;
+    z1 *= len;
+    z2 *= len;
+  }
+
+  let x0 = upy * z2 - upz * z1,
+      x1 = upz * z0 - upx * z2,
+      x2 = upx * z1 - upy * z0;
+
+  len = x0*x0 + x1*x1 + x2*x2;
+  if (len > 0) {
+    len = 1 / Math.sqrt(len);
+    x0 *= len;
+    x1 *= len;
+    x2 *= len;
+  }
+
+  out[0] = x0;
+  out[1] = x1;
+  out[2] = x2;
+  out[3] = 0;
+  out[4] = z1 * x2 - z2 * x1;
+  out[5] = z2 * x0 - z0 * x2;
+  out[6] = z0 * x1 - z1 * x0;
+  out[7] = 0;
+  out[8] = z0;
+  out[9] = z1;
+  out[10] = z2;
+  out[11] = 0;
+  out[12] = eyex;
+  out[13] = eyey;
+  out[14] = eyez;
+  out[15] = 1;
+  return out;
+};
+
+/**
+ * Returns a string representation of a mat4
+ *
+ * @param {mat4} a matrix to represent as a string
+ * @returns {String} string representation of the matrix
+ */
+export function str(a) {
+  return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
+          a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
+          a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
+          a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
+}
+
+/**
+ * Returns Frobenius norm of a mat4
+ *
+ * @param {mat4} a the matrix to calculate Frobenius norm of
+ * @returns {Number} Frobenius norm
+ */
+export function frob(a) {
+  return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2) + Math.pow(a[9], 2) + Math.pow(a[10], 2) + Math.pow(a[11], 2) + Math.pow(a[12], 2) + Math.pow(a[13], 2) + Math.pow(a[14], 2) + Math.pow(a[15], 2) ))
+}
+
+/**
+ * Adds two mat4's
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the first operand
+ * @param {mat4} b the second operand
+ * @returns {mat4} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  out[4] = a[4] + b[4];
+  out[5] = a[5] + b[5];
+  out[6] = a[6] + b[6];
+  out[7] = a[7] + b[7];
+  out[8] = a[8] + b[8];
+  out[9] = a[9] + b[9];
+  out[10] = a[10] + b[10];
+  out[11] = a[11] + b[11];
+  out[12] = a[12] + b[12];
+  out[13] = a[13] + b[13];
+  out[14] = a[14] + b[14];
+  out[15] = a[15] + b[15];
+  return out;
+}
+
+/**
+ * Subtracts matrix b from matrix a
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the first operand
+ * @param {mat4} b the second operand
+ * @returns {mat4} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  out[3] = a[3] - b[3];
+  out[4] = a[4] - b[4];
+  out[5] = a[5] - b[5];
+  out[6] = a[6] - b[6];
+  out[7] = a[7] - b[7];
+  out[8] = a[8] - b[8];
+  out[9] = a[9] - b[9];
+  out[10] = a[10] - b[10];
+  out[11] = a[11] - b[11];
+  out[12] = a[12] - b[12];
+  out[13] = a[13] - b[13];
+  out[14] = a[14] - b[14];
+  out[15] = a[15] - b[15];
+  return out;
+}
+
+/**
+ * Multiply each element of the matrix by a scalar.
+ *
+ * @param {mat4} out the receiving matrix
+ * @param {mat4} a the matrix to scale
+ * @param {Number} b amount to scale the matrix's elements by
+ * @returns {mat4} out
+ */
+export function multiplyScalar(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  out[4] = a[4] * b;
+  out[5] = a[5] * b;
+  out[6] = a[6] * b;
+  out[7] = a[7] * b;
+  out[8] = a[8] * b;
+  out[9] = a[9] * b;
+  out[10] = a[10] * b;
+  out[11] = a[11] * b;
+  out[12] = a[12] * b;
+  out[13] = a[13] * b;
+  out[14] = a[14] * b;
+  out[15] = a[15] * b;
+  return out;
+}
+
+/**
+ * Adds two mat4's after multiplying each element of the second operand by a scalar value.
+ *
+ * @param {mat4} out the receiving vector
+ * @param {mat4} a the first operand
+ * @param {mat4} b the second operand
+ * @param {Number} scale the amount to scale b's elements by before adding
+ * @returns {mat4} out
+ */
+export function multiplyScalarAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  out[3] = a[3] + (b[3] * scale);
+  out[4] = a[4] + (b[4] * scale);
+  out[5] = a[5] + (b[5] * scale);
+  out[6] = a[6] + (b[6] * scale);
+  out[7] = a[7] + (b[7] * scale);
+  out[8] = a[8] + (b[8] * scale);
+  out[9] = a[9] + (b[9] * scale);
+  out[10] = a[10] + (b[10] * scale);
+  out[11] = a[11] + (b[11] * scale);
+  out[12] = a[12] + (b[12] * scale);
+  out[13] = a[13] + (b[13] * scale);
+  out[14] = a[14] + (b[14] * scale);
+  out[15] = a[15] + (b[15] * scale);
+  return out;
+}
+
+/**
+ * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {mat4} a The first matrix.
+ * @param {mat4} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&
+         a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] &&
+         a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] &&
+         a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];
+}
+
+/**
+ * Returns whether or not the matrices have approximately the same elements in the same position.
+ *
+ * @param {mat4} a The first matrix.
+ * @param {mat4} b The second matrix.
+ * @returns {Boolean} True if the matrices are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0  = a[0],  a1  = a[1],  a2  = a[2],  a3  = a[3];
+  let a4  = a[4],  a5  = a[5],  a6  = a[6],  a7  = a[7];
+  let a8  = a[8],  a9  = a[9],  a10 = a[10], a11 = a[11];
+  let a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];
+
+  let b0  = b[0],  b1  = b[1],  b2  = b[2],  b3  = b[3];
+  let b4  = b[4],  b5  = b[5],  b6  = b[6],  b7  = b[7];
+  let b8  = b[8],  b9  = b[9],  b10 = b[10], b11 = b[11];
+  let b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
+
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
+          Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
+          Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
+          Math.abs(a6 - b6) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
+          Math.abs(a7 - b7) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
+          Math.abs(a8 - b8) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a8), Math.abs(b8)) &&
+          Math.abs(a9 - b9) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a9), Math.abs(b9)) &&
+          Math.abs(a10 - b10) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a10), Math.abs(b10)) &&
+          Math.abs(a11 - b11) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a11), Math.abs(b11)) &&
+          Math.abs(a12 - b12) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a12), Math.abs(b12)) &&
+          Math.abs(a13 - b13) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a13), Math.abs(b13)) &&
+          Math.abs(a14 - b14) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a14), Math.abs(b14)) &&
+          Math.abs(a15 - b15) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a15), Math.abs(b15)));
+}
+
+/**
+ * Alias for {@link mat4.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link mat4.subtract}
+ * @function
+ */
+export const sub = subtract;
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat.js
new file mode 100644
index 0000000..36b220d
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat.js
@@ -0,0 +1,626 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js"
+import * as mat3 from "./mat3.js"
+import * as vec3 from "./vec3.js"
+import * as vec4 from "./vec4.js"
+
+/**
+ * Quaternion
+ * @module quat
+ */
+
+/**
+ * Creates a new identity quat
+ *
+ * @returns {quat} a new quaternion
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  return out;
+}
+
+/**
+ * Set a quat to the identity quaternion
+ *
+ * @param {quat} out the receiving quaternion
+ * @returns {quat} out
+ */
+export function identity(out) {
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  return out;
+}
+
+/**
+ * Sets a quat from the given angle and rotation axis,
+ * then returns it.
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {vec3} axis the axis around which to rotate
+ * @param {Number} rad the angle in radians
+ * @returns {quat} out
+ **/
+export function setAxisAngle(out, axis, rad) {
+  rad = rad * 0.5;
+  let s = Math.sin(rad);
+  out[0] = s * axis[0];
+  out[1] = s * axis[1];
+  out[2] = s * axis[2];
+  out[3] = Math.cos(rad);
+  return out;
+}
+
+/**
+ * Gets the rotation axis and angle for a given
+ *  quaternion. If a quaternion is created with
+ *  setAxisAngle, this method will return the same
+ *  values as providied in the original parameter list
+ *  OR functionally equivalent values.
+ * Example: The quaternion formed by axis [0, 0, 1] and
+ *  angle -90 is the same as the quaternion formed by
+ *  [0, 0, 1] and 270. This method favors the latter.
+ * @param  {vec3} out_axis  Vector receiving the axis of rotation
+ * @param  {quat} q     Quaternion to be decomposed
+ * @return {Number}     Angle, in radians, of the rotation
+ */
+export function getAxisAngle(out_axis, q) {
+  let rad = Math.acos(q[3]) * 2.0;
+  let s = Math.sin(rad / 2.0);
+  if (s != 0.0) {
+    out_axis[0] = q[0] / s;
+    out_axis[1] = q[1] / s;
+    out_axis[2] = q[2] / s;
+  } else {
+    // If s is zero, return any axis (no rotation - axis does not matter)
+    out_axis[0] = 1;
+    out_axis[1] = 0;
+    out_axis[2] = 0;
+  }
+  return rad;
+}
+
+/**
+ * Multiplies two quat's
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @returns {quat} out
+ */
+export function multiply(out, a, b) {
+  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
+  let bx = b[0], by = b[1], bz = b[2], bw = b[3];
+
+  out[0] = ax * bw + aw * bx + ay * bz - az * by;
+  out[1] = ay * bw + aw * by + az * bx - ax * bz;
+  out[2] = az * bw + aw * bz + ax * by - ay * bx;
+  out[3] = aw * bw - ax * bx - ay * by - az * bz;
+  return out;
+}
+
+/**
+ * Rotates a quaternion by the given angle about the X axis
+ *
+ * @param {quat} out quat receiving operation result
+ * @param {quat} a quat to rotate
+ * @param {number} rad angle (in radians) to rotate
+ * @returns {quat} out
+ */
+export function rotateX(out, a, rad) {
+  rad *= 0.5;
+
+  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
+  let bx = Math.sin(rad), bw = Math.cos(rad);
+
+  out[0] = ax * bw + aw * bx;
+  out[1] = ay * bw + az * bx;
+  out[2] = az * bw - ay * bx;
+  out[3] = aw * bw - ax * bx;
+  return out;
+}
+
+/**
+ * Rotates a quaternion by the given angle about the Y axis
+ *
+ * @param {quat} out quat receiving operation result
+ * @param {quat} a quat to rotate
+ * @param {number} rad angle (in radians) to rotate
+ * @returns {quat} out
+ */
+export function rotateY(out, a, rad) {
+  rad *= 0.5;
+
+  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
+  let by = Math.sin(rad), bw = Math.cos(rad);
+
+  out[0] = ax * bw - az * by;
+  out[1] = ay * bw + aw * by;
+  out[2] = az * bw + ax * by;
+  out[3] = aw * bw - ay * by;
+  return out;
+}
+
+/**
+ * Rotates a quaternion by the given angle about the Z axis
+ *
+ * @param {quat} out quat receiving operation result
+ * @param {quat} a quat to rotate
+ * @param {number} rad angle (in radians) to rotate
+ * @returns {quat} out
+ */
+export function rotateZ(out, a, rad) {
+  rad *= 0.5;
+
+  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
+  let bz = Math.sin(rad), bw = Math.cos(rad);
+
+  out[0] = ax * bw + ay * bz;
+  out[1] = ay * bw - ax * bz;
+  out[2] = az * bw + aw * bz;
+  out[3] = aw * bw - az * bz;
+  return out;
+}
+
+/**
+ * Calculates the W component of a quat from the X, Y, and Z components.
+ * Assumes that quaternion is 1 unit in length.
+ * Any existing W component will be ignored.
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a quat to calculate W component of
+ * @returns {quat} out
+ */
+export function calculateW(out, a) {
+  let x = a[0], y = a[1], z = a[2];
+
+  out[0] = x;
+  out[1] = y;
+  out[2] = z;
+  out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
+  return out;
+}
+
+/**
+ * Performs a spherical linear interpolation between two quat
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {quat} out
+ */
+export function slerp(out, a, b, t) {
+  // benchmarks:
+  //    http://jsperf.com/quaternion-slerp-implementations
+  let ax = a[0], ay = a[1], az = a[2], aw = a[3];
+  let bx = b[0], by = b[1], bz = b[2], bw = b[3];
+
+  let omega, cosom, sinom, scale0, scale1;
+
+  // calc cosine
+  cosom = ax * bx + ay * by + az * bz + aw * bw;
+  // adjust signs (if necessary)
+  if ( cosom < 0.0 ) {
+    cosom = -cosom;
+    bx = - bx;
+    by = - by;
+    bz = - bz;
+    bw = - bw;
+  }
+  // calculate coefficients
+  if ( (1.0 - cosom) > 0.000001 ) {
+    // standard case (slerp)
+    omega  = Math.acos(cosom);
+    sinom  = Math.sin(omega);
+    scale0 = Math.sin((1.0 - t) * omega) / sinom;
+    scale1 = Math.sin(t * omega) / sinom;
+  } else {
+    // "from" and "to" quaternions are very close
+    //  ... so we can do a linear interpolation
+    scale0 = 1.0 - t;
+    scale1 = t;
+  }
+  // calculate final values
+  out[0] = scale0 * ax + scale1 * bx;
+  out[1] = scale0 * ay + scale1 * by;
+  out[2] = scale0 * az + scale1 * bz;
+  out[3] = scale0 * aw + scale1 * bw;
+
+  return out;
+}
+
+/**
+ * Calculates the inverse of a quat
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a quat to calculate inverse of
+ * @returns {quat} out
+ */
+export function invert(out, a) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let dot = a0*a0 + a1*a1 + a2*a2 + a3*a3;
+  let invDot = dot ? 1.0/dot : 0;
+
+  // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
+
+  out[0] = -a0*invDot;
+  out[1] = -a1*invDot;
+  out[2] = -a2*invDot;
+  out[3] = a3*invDot;
+  return out;
+}
+
+/**
+ * Calculates the conjugate of a quat
+ * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a quat to calculate conjugate of
+ * @returns {quat} out
+ */
+export function conjugate(out, a) {
+  out[0] = -a[0];
+  out[1] = -a[1];
+  out[2] = -a[2];
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Creates a quaternion from the given 3x3 rotation matrix.
+ *
+ * NOTE: The resultant quaternion is not normalized, so you should be sure
+ * to renormalize the quaternion yourself where necessary.
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {mat3} m rotation matrix
+ * @returns {quat} out
+ * @function
+ */
+export function fromMat3(out, m) {
+  // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
+  // article "Quaternion Calculus and Fast Animation".
+  let fTrace = m[0] + m[4] + m[8];
+  let fRoot;
+
+  if ( fTrace > 0.0 ) {
+    // |w| > 1/2, may as well choose w > 1/2
+    fRoot = Math.sqrt(fTrace + 1.0);  // 2w
+    out[3] = 0.5 * fRoot;
+    fRoot = 0.5/fRoot;  // 1/(4w)
+    out[0] = (m[5]-m[7])*fRoot;
+    out[1] = (m[6]-m[2])*fRoot;
+    out[2] = (m[1]-m[3])*fRoot;
+  } else {
+    // |w| <= 1/2
+    let i = 0;
+    if ( m[4] > m[0] )
+      i = 1;
+    if ( m[8] > m[i*3+i] )
+      i = 2;
+    let j = (i+1)%3;
+    let k = (i+2)%3;
+
+    fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0);
+    out[i] = 0.5 * fRoot;
+    fRoot = 0.5 / fRoot;
+    out[3] = (m[j*3+k] - m[k*3+j]) * fRoot;
+    out[j] = (m[j*3+i] + m[i*3+j]) * fRoot;
+    out[k] = (m[k*3+i] + m[i*3+k]) * fRoot;
+  }
+
+  return out;
+}
+
+/**
+ * Creates a quaternion from the given euler angle x, y, z.
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {x} Angle to rotate around X axis in degrees.
+ * @param {y} Angle to rotate around Y axis in degrees.
+ * @param {z} Angle to rotate around Z axis in degrees.
+ * @returns {quat} out
+ * @function
+ */
+export function fromEuler(out, x, y, z) {
+    let halfToRad = 0.5 * Math.PI / 180.0;
+    x *= halfToRad;
+    y *= halfToRad;
+    z *= halfToRad;
+
+    let sx = Math.sin(x);
+    let cx = Math.cos(x);
+    let sy = Math.sin(y);
+    let cy = Math.cos(y);
+    let sz = Math.sin(z);
+    let cz = Math.cos(z);
+
+    out[0] = sx * cy * cz - cx * sy * sz;
+    out[1] = cx * sy * cz + sx * cy * sz;
+    out[2] = cx * cy * sz - sx * sy * cz;
+    out[3] = cx * cy * cz + sx * sy * sz;
+
+    return out;
+}
+
+/**
+ * Returns a string representation of a quatenion
+ *
+ * @param {quat} a vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+export function str(a) {
+  return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
+}
+
+/**
+ * Creates a new quat initialized with values from an existing quaternion
+ *
+ * @param {quat} a quaternion to clone
+ * @returns {quat} a new quaternion
+ * @function
+ */
+export const clone = vec4.clone;
+
+/**
+ * Creates a new quat initialized with the given values
+ *
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @param {Number} w W component
+ * @returns {quat} a new quaternion
+ * @function
+ */
+export const fromValues = vec4.fromValues;
+
+/**
+ * Copy the values from one quat to another
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the source quaternion
+ * @returns {quat} out
+ * @function
+ */
+export const copy = vec4.copy;
+
+/**
+ * Set the components of a quat to the given values
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @param {Number} w W component
+ * @returns {quat} out
+ * @function
+ */
+export const set = vec4.set;
+
+/**
+ * Adds two quat's
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @returns {quat} out
+ * @function
+ */
+export const add = vec4.add;
+
+/**
+ * Alias for {@link quat.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Scales a quat by a scalar number
+ *
+ * @param {quat} out the receiving vector
+ * @param {quat} a the vector to scale
+ * @param {Number} b amount to scale the vector by
+ * @returns {quat} out
+ * @function
+ */
+export const scale = vec4.scale;
+
+/**
+ * Calculates the dot product of two quat's
+ *
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @returns {Number} dot product of a and b
+ * @function
+ */
+export const dot = vec4.dot;
+
+/**
+ * Performs a linear interpolation between two quat's
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {quat} out
+ * @function
+ */
+export const lerp = vec4.lerp;
+
+/**
+ * Calculates the length of a quat
+ *
+ * @param {quat} a vector to calculate length of
+ * @returns {Number} length of a
+ */
+export const length = vec4.length;
+
+/**
+ * Alias for {@link quat.length}
+ * @function
+ */
+export const len = length;
+
+/**
+ * Calculates the squared length of a quat
+ *
+ * @param {quat} a vector to calculate squared length of
+ * @returns {Number} squared length of a
+ * @function
+ */
+export const squaredLength = vec4.squaredLength;
+
+/**
+ * Alias for {@link quat.squaredLength}
+ * @function
+ */
+export const sqrLen = squaredLength;
+
+/**
+ * Normalize a quat
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a quaternion to normalize
+ * @returns {quat} out
+ * @function
+ */
+export const normalize = vec4.normalize;
+
+/**
+ * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {quat} a The first quaternion.
+ * @param {quat} b The second quaternion.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export const exactEquals = vec4.exactEquals;
+
+/**
+ * Returns whether or not the quaternions have approximately the same elements in the same position.
+ *
+ * @param {quat} a The first vector.
+ * @param {quat} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export const equals = vec4.equals;
+
+/**
+ * Sets a quaternion to represent the shortest rotation from one
+ * vector to another.
+ *
+ * Both vectors are assumed to be unit length.
+ *
+ * @param {quat} out the receiving quaternion.
+ * @param {vec3} a the initial vector
+ * @param {vec3} b the destination vector
+ * @returns {quat} out
+ */
+export const rotationTo = (function() {
+  let tmpvec3 = vec3.create();
+  let xUnitVec3 = vec3.fromValues(1,0,0);
+  let yUnitVec3 = vec3.fromValues(0,1,0);
+
+  return function(out, a, b) {
+    let dot = vec3.dot(a, b);
+    if (dot < -0.999999) {
+      vec3.cross(tmpvec3, xUnitVec3, a);
+      if (vec3.len(tmpvec3) < 0.000001)
+        vec3.cross(tmpvec3, yUnitVec3, a);
+      vec3.normalize(tmpvec3, tmpvec3);
+      setAxisAngle(out, tmpvec3, Math.PI);
+      return out;
+    } else if (dot > 0.999999) {
+      out[0] = 0;
+      out[1] = 0;
+      out[2] = 0;
+      out[3] = 1;
+      return out;
+    } else {
+      vec3.cross(tmpvec3, a, b);
+      out[0] = tmpvec3[0];
+      out[1] = tmpvec3[1];
+      out[2] = tmpvec3[2];
+      out[3] = 1 + dot;
+      return normalize(out, out);
+    }
+  };
+})();
+
+/**
+ * Performs a spherical linear interpolation with two control points
+ *
+ * @param {quat} out the receiving quaternion
+ * @param {quat} a the first operand
+ * @param {quat} b the second operand
+ * @param {quat} c the third operand
+ * @param {quat} d the fourth operand
+ * @param {Number} t interpolation amount
+ * @returns {quat} out
+ */
+export const sqlerp = (function () {
+  let temp1 = create();
+  let temp2 = create();
+
+  return function (out, a, b, c, d, t) {
+    slerp(temp1, a, d, t);
+    slerp(temp2, b, c, t);
+    slerp(out, temp1, temp2, 2 * t * (1 - t));
+
+    return out;
+  };
+}());
+
+/**
+ * Sets the specified quaternion with values corresponding to the given
+ * axes. Each axis is a vec3 and is expected to be unit length and
+ * perpendicular to all other specified axes.
+ *
+ * @param {vec3} view  the vector representing the viewing direction
+ * @param {vec3} right the vector representing the local "right" direction
+ * @param {vec3} up    the vector representing the local "up" direction
+ * @returns {quat} out
+ */
+export const setAxes = (function() {
+  let matr = mat3.create();
+
+  return function(out, view, right, up) {
+    matr[0] = right[0];
+    matr[3] = right[1];
+    matr[6] = right[2];
+
+    matr[1] = up[0];
+    matr[4] = up[1];
+    matr[7] = up[2];
+
+    matr[2] = -view[0];
+    matr[5] = -view[1];
+    matr[8] = -view[2];
+
+    return normalize(out, fromMat3(out, matr));
+  };
+})();
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat2.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat2.js
new file mode 100644
index 0000000..789e0b5
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/quat2.js
@@ -0,0 +1,859 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+
+import * as glMatrix from "./common.js";
+import * as quat from "./quat.js";
+import * as mat4 from "./mat4.js";
+
+/**
+ * Dual Quaternion<br>
+ * Format: [real, dual]<br>
+ * Quaternion format: XYZW<br>
+ * Make sure to have normalized dual quaternions, otherwise the functions may not work as intended.<br>
+ * @module quat2
+ */
+
+
+/**
+ * Creates a new identity dual quat
+ *
+ * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation]
+ */
+export function create() {
+  let dq = new glMatrix.ARRAY_TYPE(8);
+  dq[0] = 0;
+  dq[1] = 0;
+  dq[2] = 0;
+  dq[3] = 1;
+  dq[4] = 0;
+  dq[5] = 0;
+  dq[6] = 0;
+  dq[7] = 0;
+  return dq;
+}
+
+/**
+ * Creates a new quat initialized with values from an existing quaternion
+ *
+ * @param {quat2} a dual quaternion to clone
+ * @returns {quat2} new dual quaternion
+ * @function
+ */
+export function clone(a) {
+  let dq = new glMatrix.ARRAY_TYPE(8);
+  dq[0] = a[0];
+  dq[1] = a[1];
+  dq[2] = a[2];
+  dq[3] = a[3];
+  dq[4] = a[4];
+  dq[5] = a[5];
+  dq[6] = a[6];
+  dq[7] = a[7];
+  return dq;
+}
+
+/**
+ * Creates a new dual quat initialized with the given values
+ *
+ * @param {Number} x1 X component
+ * @param {Number} y1 Y component
+ * @param {Number} z1 Z component
+ * @param {Number} w1 W component
+ * @param {Number} x2 X component
+ * @param {Number} y2 Y component
+ * @param {Number} z2 Z component
+ * @param {Number} w2 W component
+ * @returns {quat2} new dual quaternion
+ * @function
+ */
+export function fromValues(x1, y1, z1, w1, x2, y2, z2, w2) {
+  let dq = new glMatrix.ARRAY_TYPE(8);
+  dq[0] = x1;
+  dq[1] = y1;
+  dq[2] = z1;
+  dq[3] = w1;
+  dq[4] = x2;
+  dq[5] = y2;
+  dq[6] = z2;
+  dq[7] = w2;
+  return dq;
+}
+
+/**
+ * Creates a new dual quat from the given values (quat and translation)
+ *
+ * @param {Number} x1 X component
+ * @param {Number} y1 Y component
+ * @param {Number} z1 Z component
+ * @param {Number} w1 W component
+ * @param {Number} x2 X component (translation)
+ * @param {Number} y2 Y component (translation)
+ * @param {Number} z2 Z component (translation)
+ * @returns {quat2} new dual quaternion
+ * @function
+ */
+export function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) {
+  let dq = new glMatrix.ARRAY_TYPE(8);
+  dq[0] = x1;
+  dq[1] = y1;
+  dq[2] = z1;
+  dq[3] = w1;
+  let ax = x2 * 0.5,
+    ay = y2 * 0.5,
+    az = z2 * 0.5;
+  dq[4] = ax * w1 + ay * z1 - az * y1;
+  dq[5] = ay * w1 + az * x1 - ax * z1;
+  dq[6] = az * w1 + ax * y1 - ay * x1;
+  dq[7] = -ax * x1 - ay * y1 - az * z1;
+  return dq;
+}
+
+/**
+ * Creates a dual quat from a quaternion and a translation
+ *
+ * @param {quat2} dual quaternion receiving operation result
+ * @param {quat} q quaternion
+ * @param {vec3} t tranlation vector
+ * @returns {quat2} dual quaternion receiving operation result
+ * @function
+ */
+export function fromRotationTranslation(out, q, t) {
+  let ax = t[0] * 0.5,
+    ay = t[1] * 0.5,
+    az = t[2] * 0.5,
+    bx = q[0],
+    by = q[1],
+    bz = q[2],
+    bw = q[3];
+  out[0] = bx;
+  out[1] = by;
+  out[2] = bz;
+  out[3] = bw;
+  out[4] = ax * bw + ay * bz - az * by;
+  out[5] = ay * bw + az * bx - ax * bz;
+  out[6] = az * bw + ax * by - ay * bx;
+  out[7] = -ax * bx - ay * by - az * bz;
+  return out;
+}
+
+/**
+ * Creates a dual quat from a translation
+ *
+ * @param {quat2} dual quaternion receiving operation result
+ * @param {vec3} t translation vector
+ * @returns {quat2} dual quaternion receiving operation result
+ * @function
+ */
+export function fromTranslation(out, t) {
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  out[4] = t[0] * 0.5;
+  out[5] = t[1] * 0.5;
+  out[6] = t[2] * 0.5;
+  out[7] = 0;
+  return out;
+}
+
+/**
+ * Creates a dual quat from a quaternion
+ *
+ * @param {quat2} dual quaternion receiving operation result
+ * @param {quat} q the quaternion
+ * @returns {quat2} dual quaternion receiving operation result
+ * @function
+ */
+export function fromRotation(out, q) {
+  out[0] = q[0];
+  out[1] = q[1];
+  out[2] = q[2];
+  out[3] = q[3];
+  out[4] = 0;
+  out[5] = 0;
+  out[6] = 0;
+  out[7] = 0;
+  return out;
+}
+
+/**
+ * Creates a new dual quat from a matrix (4x4)
+ * 
+ * @param {quat2} out the dual quaternion
+ * @param {mat4} a the matrix
+ * @returns {quat2} dual quat receiving operation result
+ * @function
+ */
+export function fromMat4(out, a) {
+  //TODO Optimize this 
+  let outer = quat.create();
+  mat4.getRotation(outer, a);
+  let t = new glMatrix.ARRAY_TYPE(3);
+  mat4.getTranslation(t, a);
+  fromRotationTranslation(out, outer, t);
+  return out;
+}
+
+/**
+ * Copy the values from one dual quat to another
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the source dual quaternion
+ * @returns {quat2} out
+ * @function
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  out[4] = a[4];
+  out[5] = a[5];
+  out[6] = a[6];
+  out[7] = a[7];
+  return out;
+}
+
+/**
+ * Set a dual quat to the identity dual quaternion
+ *
+ * @param {quat2} out the receiving quaternion
+ * @returns {quat2} out
+ */
+export function identity(out) {
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 1;
+  out[4] = 0;
+  out[5] = 0;
+  out[6] = 0;
+  out[7] = 0;
+  return out;
+}
+
+/**
+ * Set the components of a dual quat to the given values
+ *
+ * @param {quat2} out the receiving quaternion
+ * @param {Number} x1 X component
+ * @param {Number} y1 Y component
+ * @param {Number} z1 Z component
+ * @param {Number} w1 W component
+ * @param {Number} x2 X component
+ * @param {Number} y2 Y component
+ * @param {Number} z2 Z component
+ * @param {Number} w2 W component
+ * @returns {quat2} out
+ * @function
+ */
+export function set(out, x1, y1, z1, w1, x2, y2, z2, w2) {
+  out[0] = x1;
+  out[1] = y1;
+  out[2] = z1;
+  out[3] = w1;
+
+  out[4] = x2;
+  out[5] = y2;
+  out[6] = z2;
+  out[7] = w2;
+  return out;
+}
+
+/**
+ * Gets the real part of a dual quat
+ * @param  {quat} out real part
+ * @param  {quat2} a Dual Quaternion
+ * @return {quat} real part
+ */
+export const getReal = quat.copy;
+
+/**
+ * Gets the dual part of a dual quat
+ * @param  {quat} out dual part
+ * @param  {quat2} a Dual Quaternion
+ * @return {quat} dual part
+ */
+export function getDual(out, a) {
+  out[0] = a[4];
+  out[1] = a[5];
+  out[2] = a[6];
+  out[3] = a[7];
+  return out;
+}
+
+/**
+ * Set the real component of a dual quat to the given quaternion
+ *
+ * @param {quat2} out the receiving quaternion
+ * @param {quat} q a quaternion representing the real part
+ * @returns {quat2} out
+ * @function
+ */
+export const setReal = quat.copy;
+
+/**
+ * Set the dual component of a dual quat to the given quaternion
+ *
+ * @param {quat2} out the receiving quaternion
+ * @param {quat} q a quaternion representing the dual part
+ * @returns {quat2} out
+ * @function
+ */
+export function setDual(out, q) {
+  out[4] = q[0];
+  out[5] = q[1];
+  out[6] = q[2];
+  out[7] = q[3];
+  return out;
+}
+
+/**
+ * Gets the translation of a normalized dual quat
+ * @param  {vec3} out translation
+ * @param  {quat2} a Dual Quaternion to be decomposed
+ * @return {vec3} translation
+ */
+export function getTranslation(out, a) {
+  let ax = a[4],
+    ay = a[5],
+    az = a[6],
+    aw = a[7],
+    bx = -a[0],
+    by = -a[1],
+    bz = -a[2],
+    bw = a[3];
+  out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
+  out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
+  out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
+  return out;
+}
+
+/**
+ * Translates a dual quat by the given vector
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to translate
+ * @param {vec3} v vector to translate by
+ * @returns {quat2} out
+ */
+export function translate(out, a, v) {
+  let ax1 = a[0],
+    ay1 = a[1],
+    az1 = a[2],
+    aw1 = a[3],
+    bx1 = v[0] * 0.5,
+    by1 = v[1] * 0.5,
+    bz1 = v[2] * 0.5,
+    ax2 = a[4],
+    ay2 = a[5],
+    az2 = a[6],
+    aw2 = a[7];
+  out[0] = ax1;
+  out[1] = ay1;
+  out[2] = az1;
+  out[3] = aw1;
+  out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2;
+  out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2;
+  out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2;
+  out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2;
+  return out;
+}
+
+/**
+ * Rotates a dual quat around the X axis
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to rotate
+ * @param {number} rad how far should the rotation be
+ * @returns {quat2} out
+ */
+export function rotateX(out, a, rad) {
+  let bx = -a[0],
+    by = -a[1],
+    bz = -a[2],
+    bw = a[3],
+    ax = a[4],
+    ay = a[5],
+    az = a[6],
+    aw = a[7],
+    ax1 = ax * bw + aw * bx + ay * bz - az * by,
+    ay1 = ay * bw + aw * by + az * bx - ax * bz,
+    az1 = az * bw + aw * bz + ax * by - ay * bx,
+    aw1 = aw * bw - ax * bx - ay * by - az * bz;
+  quat.rotateX(out, a, rad);
+  bx = out[0];
+  by = out[1];
+  bz = out[2];
+  bw = out[3];
+  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+  return out;
+}
+
+/**
+ * Rotates a dual quat around the Y axis
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to rotate
+ * @param {number} rad how far should the rotation be
+ * @returns {quat2} out
+ */
+export function rotateY(out, a, rad) {
+  let bx = -a[0],
+    by = -a[1],
+    bz = -a[2],
+    bw = a[3],
+    ax = a[4],
+    ay = a[5],
+    az = a[6],
+    aw = a[7],
+    ax1 = ax * bw + aw * bx + ay * bz - az * by,
+    ay1 = ay * bw + aw * by + az * bx - ax * bz,
+    az1 = az * bw + aw * bz + ax * by - ay * bx,
+    aw1 = aw * bw - ax * bx - ay * by - az * bz;
+  quat.rotateY(out, a, rad);
+  bx = out[0];
+  by = out[1];
+  bz = out[2];
+  bw = out[3];
+  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+  return out;
+}
+
+/**
+ * Rotates a dual quat around the Z axis
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to rotate
+ * @param {number} rad how far should the rotation be
+ * @returns {quat2} out
+ */
+export function rotateZ(out, a, rad) {
+  let bx = -a[0],
+    by = -a[1],
+    bz = -a[2],
+    bw = a[3],
+    ax = a[4],
+    ay = a[5],
+    az = a[6],
+    aw = a[7],
+    ax1 = ax * bw + aw * bx + ay * bz - az * by,
+    ay1 = ay * bw + aw * by + az * bx - ax * bz,
+    az1 = az * bw + aw * bz + ax * by - ay * bx,
+    aw1 = aw * bw - ax * bx - ay * by - az * bz;
+  quat.rotateZ(out, a, rad);
+  bx = out[0];
+  by = out[1];
+  bz = out[2];
+  bw = out[3];
+  out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+  out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+  out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+  out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+  return out;
+}
+
+/**
+ * Rotates a dual quat by a given quaternion (a * q)
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to rotate
+ * @param {quat} q quaternion to rotate by
+ * @returns {quat2} out
+ */
+export function rotateByQuatAppend(out, a, q) {
+  let qx = q[0],
+    qy = q[1],
+    qz = q[2],
+    qw = q[3],
+    ax = a[0],
+    ay = a[1],
+    az = a[2],
+    aw = a[3];
+
+  out[0] = ax * qw + aw * qx + ay * qz - az * qy;
+  out[1] = ay * qw + aw * qy + az * qx - ax * qz;
+  out[2] = az * qw + aw * qz + ax * qy - ay * qx;
+  out[3] = aw * qw - ax * qx - ay * qy - az * qz;
+  ax = a[4];
+  ay = a[5];
+  az = a[6];
+  aw = a[7];
+  out[4] = ax * qw + aw * qx + ay * qz - az * qy;
+  out[5] = ay * qw + aw * qy + az * qx - ax * qz;
+  out[6] = az * qw + aw * qz + ax * qy - ay * qx;
+  out[7] = aw * qw - ax * qx - ay * qy - az * qz;
+  return out;
+}
+
+/**
+ * Rotates a dual quat by a given quaternion (q * a)
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat} q quaternion to rotate by
+ * @param {quat2} a the dual quaternion to rotate
+ * @returns {quat2} out
+ */
+export function rotateByQuatPrepend(out, q, a) {
+  let qx = q[0],
+    qy = q[1],
+    qz = q[2],
+    qw = q[3],
+    bx = a[0],
+    by = a[1],
+    bz = a[2],
+    bw = a[3];
+
+  out[0] = qx * bw + qw * bx + qy * bz - qz * by;
+  out[1] = qy * bw + qw * by + qz * bx - qx * bz;
+  out[2] = qz * bw + qw * bz + qx * by - qy * bx;
+  out[3] = qw * bw - qx * bx - qy * by - qz * bz;
+  bx = a[4];
+  by = a[5];
+  bz = a[6];
+  bw = a[7];
+  out[4] = qx * bw + qw * bx + qy * bz - qz * by;
+  out[5] = qy * bw + qw * by + qz * bx - qx * bz;
+  out[6] = qz * bw + qw * bz + qx * by - qy * bx;
+  out[7] = qw * bw - qx * bx - qy * by - qz * bz;
+  return out;
+}
+
+/**
+ * Rotates a dual quat around a given axis. Does the normalisation automatically
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the dual quaternion to rotate
+ * @param {vec3} axis the axis to rotate around
+ * @param {Number} rad how far the rotation should be
+ * @returns {quat2} out
+ */
+export function rotateAroundAxis(out, a, axis, rad) {
+  //Special case for rad = 0
+  if (Math.abs(rad) < glMatrix.EPSILON) {
+    return copy(out, a);
+  }
+  let axisLength = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
+
+  rad = rad * 0.5;
+  let s = Math.sin(rad);
+  let bx = s * axis[0] / axisLength;
+  let by = s * axis[1] / axisLength;
+  let bz = s * axis[2] / axisLength;
+  let bw = Math.cos(rad);
+
+  let ax1 = a[0],
+    ay1 = a[1],
+    az1 = a[2],
+    aw1 = a[3];
+  out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+  out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+  out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+  out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+
+  let ax = a[4],
+    ay = a[5],
+    az = a[6],
+    aw = a[7];
+  out[4] = ax * bw + aw * bx + ay * bz - az * by;
+  out[5] = ay * bw + aw * by + az * bx - ax * bz;
+  out[6] = az * bw + aw * bz + ax * by - ay * bx;
+  out[7] = aw * bw - ax * bx - ay * by - az * bz;
+
+  return out;
+}
+
+/**
+ * Adds two dual quat's
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the first operand
+ * @param {quat2} b the second operand
+ * @returns {quat2} out
+ * @function
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  out[4] = a[4] + b[4];
+  out[5] = a[5] + b[5];
+  out[6] = a[6] + b[6];
+  out[7] = a[7] + b[7];
+  return out;
+}
+
+/**
+ * Multiplies two dual quat's
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a the first operand
+ * @param {quat2} b the second operand
+ * @returns {quat2} out
+ */
+export function multiply(out, a, b) {
+  let ax0 = a[0],
+    ay0 = a[1],
+    az0 = a[2],
+    aw0 = a[3],
+    bx1 = b[4],
+    by1 = b[5],
+    bz1 = b[6],
+    bw1 = b[7],
+    ax1 = a[4],
+    ay1 = a[5],
+    az1 = a[6],
+    aw1 = a[7],
+    bx0 = b[0],
+    by0 = b[1],
+    bz0 = b[2],
+    bw0 = b[3];
+  out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0;
+  out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0;
+  out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0;
+  out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0;
+  out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0;
+  out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0;
+  out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0;
+  out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0;
+  return out;
+}
+
+/**
+ * Alias for {@link quat2.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Scales a dual quat by a scalar number
+ *
+ * @param {quat2} out the receiving dual quat
+ * @param {quat2} a the dual quat to scale
+ * @param {Number} b amount to scale the dual quat by
+ * @returns {quat2} out
+ * @function
+ */
+export function scale(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  out[4] = a[4] * b;
+  out[5] = a[5] * b;
+  out[6] = a[6] * b;
+  out[7] = a[7] * b;
+  return out;
+}
+
+/**
+ * Calculates the dot product of two dual quat's (The dot product of the real parts)
+ *
+ * @param {quat2} a the first operand
+ * @param {quat2} b the second operand
+ * @returns {Number} dot product of a and b
+ * @function
+ */
+export const dot = quat.dot;
+
+/**
+ * Performs a linear interpolation between two dual quats's
+ * NOTE: The resulting dual quaternions won't always be normalized (The error is most noticeable when t = 0.5)
+ *
+ * @param {quat2} out the receiving dual quat
+ * @param {quat2} a the first operand
+ * @param {quat2} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {quat2} out
+ */
+export function lerp(out, a, b, t) {
+  let mt = 1 - t;
+  if (dot(a, b) < 0) t = -t;
+
+  out[0] = a[0] * mt + b[0] * t;
+  out[1] = a[1] * mt + b[1] * t;
+  out[2] = a[2] * mt + b[2] * t;
+  out[3] = a[3] * mt + b[3] * t;
+  out[4] = a[4] * mt + b[4] * t;
+  out[5] = a[5] * mt + b[5] * t;
+  out[6] = a[6] * mt + b[6] * t;
+  out[7] = a[7] * mt + b[7] * t;
+
+  return out;
+}
+
+/**
+ * Calculates the inverse of a dual quat. If they are normalized, conjugate is cheaper
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a dual quat to calculate inverse of
+ * @returns {quat2} out
+ */
+export function invert(out, a) {
+  let sqlen = squaredLength(a);
+  out[0] = -a[0] / sqlen;
+  out[1] = -a[1] / sqlen;
+  out[2] = -a[2] / sqlen;
+  out[3] = a[3] / sqlen;
+  out[4] = -a[4] / sqlen;
+  out[5] = -a[5] / sqlen;
+  out[6] = -a[6] / sqlen;
+  out[7] = a[7] / sqlen;
+  return out;
+}
+
+/**
+ * Calculates the conjugate of a dual quat
+ * If the dual quaternion is normalized, this function is faster than quat2.inverse and produces the same result.
+ *
+ * @param {quat2} out the receiving quaternion
+ * @param {quat2} a quat to calculate conjugate of
+ * @returns {quat2} out
+ */
+export function conjugate(out, a) {
+  out[0] = -a[0];
+  out[1] = -a[1];
+  out[2] = -a[2];
+  out[3] = a[3];
+  out[4] = -a[4];
+  out[5] = -a[5];
+  out[6] = -a[6];
+  out[7] = a[7];
+  return out;
+}
+
+/**
+ * Calculates the length of a dual quat
+ *
+ * @param {quat2} a dual quat to calculate length of
+ * @returns {Number} length of a
+ * @function
+ */
+export const length = quat.length;
+
+/**
+ * Alias for {@link quat2.length}
+ * @function
+ */
+export const len = length;
+
+/**
+ * Calculates the squared length of a dual quat
+ *
+ * @param {quat2} a dual quat to calculate squared length of
+ * @returns {Number} squared length of a
+ * @function
+ */
+export const squaredLength = quat.squaredLength;
+
+/**
+ * Alias for {@link quat2.squaredLength}
+ * @function
+ */
+export const sqrLen = squaredLength;
+
+/**
+ * Normalize a dual quat
+ *
+ * @param {quat2} out the receiving dual quaternion
+ * @param {quat2} a dual quaternion to normalize
+ * @returns {quat2} out
+ * @function
+ */
+export function normalize(out, a) {
+  let magnitude = squaredLength(a);
+  if (magnitude > 0) {
+    magnitude = Math.sqrt(magnitude);
+    out[0] = a[0] / magnitude;
+    out[1] = a[1] / magnitude;
+    out[2] = a[2] / magnitude;
+    out[3] = a[3] / magnitude;
+    out[4] = a[4] / magnitude;
+    out[5] = a[5] / magnitude;
+    out[6] = a[6] / magnitude;
+    out[7] = a[7] / magnitude;
+  }
+  return out;
+}
+
+/**
+ * Returns a string representation of a dual quatenion
+ *
+ * @param {quat2} a dual quaternion to represent as a string
+ * @returns {String} string representation of the dual quat
+ */
+export function str(a) {
+  return 'quat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
+    a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ')';
+}
+
+/**
+ * Returns whether or not the dual quaternions have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {quat2} a the first dual quaternion.
+ * @param {quat2} b the second dual quaternion.
+ * @returns {Boolean} true if the dual quaternions are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] &&
+    a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7];
+}
+
+/**
+ * Returns whether or not the dual quaternions have approximately the same elements in the same position.
+ *
+ * @param {quat2} a the first dual quat.
+ * @param {quat2} b the second dual quat.
+ * @returns {Boolean} true if the dual quats are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0],
+    a1 = a[1],
+    a2 = a[2],
+    a3 = a[3],
+    a4 = a[4],
+    a5 = a[5],
+    a6 = a[6],
+    a7 = a[7];
+  let b0 = b[0],
+    b1 = b[1],
+    b2 = b[2],
+    b3 = b[3],
+    b4 = b[4],
+    b5 = b[5],
+    b6 = b[6],
+    b7 = b[7];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+    Math.abs(a1 - b1) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+    Math.abs(a2 - b2) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+    Math.abs(a3 - b3) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
+    Math.abs(a4 - b4) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
+    Math.abs(a5 - b5) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
+    Math.abs(a6 - b6) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
+    Math.abs(a7 - b7) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)));
+}
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec2.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec2.js
new file mode 100644
index 0000000..1be4a93
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec2.js
@@ -0,0 +1,584 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * 2 Dimensional Vector
+ * @module vec2
+ */
+
+/**
+ * Creates a new, empty vec2
+ *
+ * @returns {vec2} a new 2D vector
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(2);
+  out[0] = 0;
+  out[1] = 0;
+  return out;
+}
+
+/**
+ * Creates a new vec2 initialized with values from an existing vector
+ *
+ * @param {vec2} a vector to clone
+ * @returns {vec2} a new 2D vector
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(2);
+  out[0] = a[0];
+  out[1] = a[1];
+  return out;
+}
+
+/**
+ * Creates a new vec2 initialized with the given values
+ *
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @returns {vec2} a new 2D vector
+ */
+export function fromValues(x, y) {
+  let out = new glMatrix.ARRAY_TYPE(2);
+  out[0] = x;
+  out[1] = y;
+  return out;
+}
+
+/**
+ * Copy the values from one vec2 to another
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the source vector
+ * @returns {vec2} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  return out;
+}
+
+/**
+ * Set the components of a vec2 to the given values
+ *
+ * @param {vec2} out the receiving vector
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @returns {vec2} out
+ */
+export function set(out, x, y) {
+  out[0] = x;
+  out[1] = y;
+  return out;
+}
+
+/**
+ * Adds two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  return out;
+}
+
+/**
+ * Subtracts vector b from vector a
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  return out;
+}
+
+/**
+ * Multiplies two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function multiply(out, a, b) {
+  out[0] = a[0] * b[0];
+  out[1] = a[1] * b[1];
+  return out;
+};
+
+/**
+ * Divides two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function divide(out, a, b) {
+  out[0] = a[0] / b[0];
+  out[1] = a[1] / b[1];
+  return out;
+};
+
+/**
+ * Math.ceil the components of a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to ceil
+ * @returns {vec2} out
+ */
+export function ceil(out, a) {
+  out[0] = Math.ceil(a[0]);
+  out[1] = Math.ceil(a[1]);
+  return out;
+};
+
+/**
+ * Math.floor the components of a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to floor
+ * @returns {vec2} out
+ */
+export function floor(out, a) {
+  out[0] = Math.floor(a[0]);
+  out[1] = Math.floor(a[1]);
+  return out;
+};
+
+/**
+ * Returns the minimum of two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function min(out, a, b) {
+  out[0] = Math.min(a[0], b[0]);
+  out[1] = Math.min(a[1], b[1]);
+  return out;
+};
+
+/**
+ * Returns the maximum of two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec2} out
+ */
+export function max(out, a, b) {
+  out[0] = Math.max(a[0], b[0]);
+  out[1] = Math.max(a[1], b[1]);
+  return out;
+};
+
+/**
+ * Math.round the components of a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to round
+ * @returns {vec2} out
+ */
+export function round (out, a) {
+  out[0] = Math.round(a[0]);
+  out[1] = Math.round(a[1]);
+  return out;
+};
+
+/**
+ * Scales a vec2 by a scalar number
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to scale
+ * @param {Number} b amount to scale the vector by
+ * @returns {vec2} out
+ */
+export function scale(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  return out;
+};
+
+/**
+ * Adds two vec2's after scaling the second operand by a scalar value
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @param {Number} scale the amount to scale b by before adding
+ * @returns {vec2} out
+ */
+export function scaleAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  return out;
+};
+
+/**
+ * Calculates the euclidian distance between two vec2's
+ *
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {Number} distance between a and b
+ */
+export function distance(a, b) {
+  var x = b[0] - a[0],
+    y = b[1] - a[1];
+  return Math.sqrt(x*x + y*y);
+};
+
+/**
+ * Calculates the squared euclidian distance between two vec2's
+ *
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {Number} squared distance between a and b
+ */
+export function squaredDistance(a, b) {
+  var x = b[0] - a[0],
+    y = b[1] - a[1];
+  return x*x + y*y;
+};
+
+/**
+ * Calculates the length of a vec2
+ *
+ * @param {vec2} a vector to calculate length of
+ * @returns {Number} length of a
+ */
+export function length(a) {
+  var x = a[0],
+    y = a[1];
+  return Math.sqrt(x*x + y*y);
+};
+
+/**
+ * Calculates the squared length of a vec2
+ *
+ * @param {vec2} a vector to calculate squared length of
+ * @returns {Number} squared length of a
+ */
+export function squaredLength (a) {
+  var x = a[0],
+    y = a[1];
+  return x*x + y*y;
+};
+
+/**
+ * Negates the components of a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to negate
+ * @returns {vec2} out
+ */
+export function negate(out, a) {
+  out[0] = -a[0];
+  out[1] = -a[1];
+  return out;
+};
+
+/**
+ * Returns the inverse of the components of a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to invert
+ * @returns {vec2} out
+ */
+export function inverse(out, a) {
+  out[0] = 1.0 / a[0];
+  out[1] = 1.0 / a[1];
+  return out;
+};
+
+/**
+ * Normalize a vec2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a vector to normalize
+ * @returns {vec2} out
+ */
+export function normalize(out, a) {
+  var x = a[0],
+    y = a[1];
+  var len = x*x + y*y;
+  if (len > 0) {
+    //TODO: evaluate use of glm_invsqrt here?
+    len = 1 / Math.sqrt(len);
+    out[0] = a[0] * len;
+    out[1] = a[1] * len;
+  }
+  return out;
+};
+
+/**
+ * Calculates the dot product of two vec2's
+ *
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {Number} dot product of a and b
+ */
+export function dot(a, b) {
+  return a[0] * b[0] + a[1] * b[1];
+};
+
+/**
+ * Computes the cross product of two vec2's
+ * Note that the cross product must by definition produce a 3D vector
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec3} out
+ */
+export function cross(out, a, b) {
+  var z = a[0] * b[1] - a[1] * b[0];
+  out[0] = out[1] = 0;
+  out[2] = z;
+  return out;
+};
+
+/**
+ * Performs a linear interpolation between two vec2's
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec2} out
+ */
+export function lerp(out, a, b, t) {
+  var ax = a[0],
+    ay = a[1];
+  out[0] = ax + t * (b[0] - ax);
+  out[1] = ay + t * (b[1] - ay);
+  return out;
+};
+
+/**
+ * Generates a random vector with the given scale
+ *
+ * @param {vec2} out the receiving vector
+ * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+ * @returns {vec2} out
+ */
+export function random(out, scale) {
+  scale = scale || 1.0;
+  var r = glMatrix.RANDOM() * 2.0 * Math.PI;
+  out[0] = Math.cos(r) * scale;
+  out[1] = Math.sin(r) * scale;
+  return out;
+};
+
+/**
+ * Transforms the vec2 with a mat2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat2} m matrix to transform with
+ * @returns {vec2} out
+ */
+export function transformMat2(out, a, m) {
+  var x = a[0],
+    y = a[1];
+  out[0] = m[0] * x + m[2] * y;
+  out[1] = m[1] * x + m[3] * y;
+  return out;
+};
+
+/**
+ * Transforms the vec2 with a mat2d
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat2d} m matrix to transform with
+ * @returns {vec2} out
+ */
+export function transformMat2d(out, a, m) {
+  var x = a[0],
+    y = a[1];
+  out[0] = m[0] * x + m[2] * y + m[4];
+  out[1] = m[1] * x + m[3] * y + m[5];
+  return out;
+};
+
+/**
+ * Transforms the vec2 with a mat3
+ * 3rd vector component is implicitly '1'
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat3} m matrix to transform with
+ * @returns {vec2} out
+ */
+export function transformMat3(out, a, m) {
+  var x = a[0],
+    y = a[1];
+  out[0] = m[0] * x + m[3] * y + m[6];
+  out[1] = m[1] * x + m[4] * y + m[7];
+  return out;
+};
+
+/**
+ * Transforms the vec2 with a mat4
+ * 3rd vector component is implicitly '0'
+ * 4th vector component is implicitly '1'
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat4} m matrix to transform with
+ * @returns {vec2} out
+ */
+export function transformMat4(out, a, m) {
+  let x = a[0];
+  let y = a[1];
+  out[0] = m[0] * x + m[4] * y + m[12];
+  out[1] = m[1] * x + m[5] * y + m[13];
+  return out;
+}
+
+/**
+ * Returns a string representation of a vector
+ *
+ * @param {vec2} a vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+export function str(a) {
+  return 'vec2(' + a[0] + ', ' + a[1] + ')';
+}
+
+/**
+ * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===)
+ *
+ * @param {vec2} a The first vector.
+ * @param {vec2} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1];
+}
+
+/**
+ * Returns whether or not the vectors have approximately the same elements in the same position.
+ *
+ * @param {vec2} a The first vector.
+ * @param {vec2} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1];
+  let b0 = b[0], b1 = b[1];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)));
+}
+
+/**
+ * Alias for {@link vec2.length}
+ * @function
+ */
+export const len = length;
+
+/**
+ * Alias for {@link vec2.subtract}
+ * @function
+ */
+export const sub = subtract;
+
+/**
+ * Alias for {@link vec2.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link vec2.divide}
+ * @function
+ */
+export const div = divide;
+
+/**
+ * Alias for {@link vec2.distance}
+ * @function
+ */
+export const dist = distance;
+
+/**
+ * Alias for {@link vec2.squaredDistance}
+ * @function
+ */
+export const sqrDist = squaredDistance;
+
+/**
+ * Alias for {@link vec2.squaredLength}
+ * @function
+ */
+export const sqrLen = squaredLength;
+
+/**
+ * Perform some operation over an array of vec2s.
+ *
+ * @param {Array} a the array of vectors to iterate over
+ * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
+ * @param {Number} offset Number of elements to skip at the beginning of the array
+ * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
+ * @param {Function} fn Function to call for each vector in the array
+ * @param {Object} [arg] additional argument to pass to fn
+ * @returns {Array} a
+ * @function
+ */
+export const forEach = (function() {
+  let vec = create();
+
+  return function(a, stride, offset, count, fn, arg) {
+    let i, l;
+    if(!stride) {
+      stride = 2;
+    }
+
+    if(!offset) {
+      offset = 0;
+    }
+
+    if(count) {
+      l = Math.min((count * stride) + offset, a.length);
+    } else {
+      l = a.length;
+    }
+
+    for(i = offset; i < l; i += stride) {
+      vec[0] = a[i]; vec[1] = a[i+1];
+      fn(vec, vec, arg);
+      a[i] = vec[0]; a[i+1] = vec[1];
+    }
+
+    return a;
+  };
+})();
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec3.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec3.js
new file mode 100644
index 0000000..ba93c0f
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec3.js
@@ -0,0 +1,787 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * 3 Dimensional Vector
+ * @module vec3
+ */
+
+/**
+ * Creates a new, empty vec3
+ *
+ * @returns {vec3} a new 3D vector
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(3);
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  return out;
+}
+
+/**
+ * Creates a new vec3 initialized with values from an existing vector
+ *
+ * @param {vec3} a vector to clone
+ * @returns {vec3} a new 3D vector
+ */
+export function clone(a) {
+  var out = new glMatrix.ARRAY_TYPE(3);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  return out;
+}
+
+/**
+ * Calculates the length of a vec3
+ *
+ * @param {vec3} a vector to calculate length of
+ * @returns {Number} length of a
+ */
+export function length(a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  return Math.sqrt(x*x + y*y + z*z);
+}
+
+/**
+ * Creates a new vec3 initialized with the given values
+ *
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @returns {vec3} a new 3D vector
+ */
+export function fromValues(x, y, z) {
+  let out = new glMatrix.ARRAY_TYPE(3);
+  out[0] = x;
+  out[1] = y;
+  out[2] = z;
+  return out;
+}
+
+/**
+ * Copy the values from one vec3 to another
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the source vector
+ * @returns {vec3} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  return out;
+}
+
+/**
+ * Set the components of a vec3 to the given values
+ *
+ * @param {vec3} out the receiving vector
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @returns {vec3} out
+ */
+export function set(out, x, y, z) {
+  out[0] = x;
+  out[1] = y;
+  out[2] = z;
+  return out;
+}
+
+/**
+ * Adds two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  return out;
+}
+
+/**
+ * Subtracts vector b from vector a
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  return out;
+}
+
+/**
+ * Multiplies two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function multiply(out, a, b) {
+  out[0] = a[0] * b[0];
+  out[1] = a[1] * b[1];
+  out[2] = a[2] * b[2];
+  return out;
+}
+
+/**
+ * Divides two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function divide(out, a, b) {
+  out[0] = a[0] / b[0];
+  out[1] = a[1] / b[1];
+  out[2] = a[2] / b[2];
+  return out;
+}
+
+/**
+ * Math.ceil the components of a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to ceil
+ * @returns {vec3} out
+ */
+export function ceil(out, a) {
+  out[0] = Math.ceil(a[0]);
+  out[1] = Math.ceil(a[1]);
+  out[2] = Math.ceil(a[2]);
+  return out;
+}
+
+/**
+ * Math.floor the components of a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to floor
+ * @returns {vec3} out
+ */
+export function floor(out, a) {
+  out[0] = Math.floor(a[0]);
+  out[1] = Math.floor(a[1]);
+  out[2] = Math.floor(a[2]);
+  return out;
+}
+
+/**
+ * Returns the minimum of two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function min(out, a, b) {
+  out[0] = Math.min(a[0], b[0]);
+  out[1] = Math.min(a[1], b[1]);
+  out[2] = Math.min(a[2], b[2]);
+  return out;
+}
+
+/**
+ * Returns the maximum of two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function max(out, a, b) {
+  out[0] = Math.max(a[0], b[0]);
+  out[1] = Math.max(a[1], b[1]);
+  out[2] = Math.max(a[2], b[2]);
+  return out;
+}
+
+/**
+ * Math.round the components of a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to round
+ * @returns {vec3} out
+ */
+export function round(out, a) {
+  out[0] = Math.round(a[0]);
+  out[1] = Math.round(a[1]);
+  out[2] = Math.round(a[2]);
+  return out;
+}
+
+/**
+ * Scales a vec3 by a scalar number
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the vector to scale
+ * @param {Number} b amount to scale the vector by
+ * @returns {vec3} out
+ */
+export function scale(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  return out;
+}
+
+/**
+ * Adds two vec3's after scaling the second operand by a scalar value
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @param {Number} scale the amount to scale b by before adding
+ * @returns {vec3} out
+ */
+export function scaleAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  return out;
+}
+
+/**
+ * Calculates the euclidian distance between two vec3's
+ *
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {Number} distance between a and b
+ */
+export function distance(a, b) {
+  let x = b[0] - a[0];
+  let y = b[1] - a[1];
+  let z = b[2] - a[2];
+  return Math.sqrt(x*x + y*y + z*z);
+}
+
+/**
+ * Calculates the squared euclidian distance between two vec3's
+ *
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {Number} squared distance between a and b
+ */
+export function squaredDistance(a, b) {
+  let x = b[0] - a[0];
+  let y = b[1] - a[1];
+  let z = b[2] - a[2];
+  return x*x + y*y + z*z;
+}
+
+/**
+ * Calculates the squared length of a vec3
+ *
+ * @param {vec3} a vector to calculate squared length of
+ * @returns {Number} squared length of a
+ */
+export function squaredLength(a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  return x*x + y*y + z*z;
+}
+
+/**
+ * Negates the components of a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to negate
+ * @returns {vec3} out
+ */
+export function negate(out, a) {
+  out[0] = -a[0];
+  out[1] = -a[1];
+  out[2] = -a[2];
+  return out;
+}
+
+/**
+ * Returns the inverse of the components of a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to invert
+ * @returns {vec3} out
+ */
+export function inverse(out, a) {
+  out[0] = 1.0 / a[0];
+  out[1] = 1.0 / a[1];
+  out[2] = 1.0 / a[2];
+  return out;
+}
+
+/**
+ * Normalize a vec3
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a vector to normalize
+ * @returns {vec3} out
+ */
+export function normalize(out, a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  let len = x*x + y*y + z*z;
+  if (len > 0) {
+    //TODO: evaluate use of glm_invsqrt here?
+    len = 1 / Math.sqrt(len);
+    out[0] = a[0] * len;
+    out[1] = a[1] * len;
+    out[2] = a[2] * len;
+  }
+  return out;
+}
+
+/**
+ * Calculates the dot product of two vec3's
+ *
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {Number} dot product of a and b
+ */
+export function dot(a, b) {
+  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+}
+
+/**
+ * Computes the cross product of two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @returns {vec3} out
+ */
+export function cross(out, a, b) {
+  let ax = a[0], ay = a[1], az = a[2];
+  let bx = b[0], by = b[1], bz = b[2];
+
+  out[0] = ay * bz - az * by;
+  out[1] = az * bx - ax * bz;
+  out[2] = ax * by - ay * bx;
+  return out;
+}
+
+/**
+ * Performs a linear interpolation between two vec3's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec3} out
+ */
+export function lerp(out, a, b, t) {
+  let ax = a[0];
+  let ay = a[1];
+  let az = a[2];
+  out[0] = ax + t * (b[0] - ax);
+  out[1] = ay + t * (b[1] - ay);
+  out[2] = az + t * (b[2] - az);
+  return out;
+}
+
+/**
+ * Performs a hermite interpolation with two control points
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @param {vec3} c the third operand
+ * @param {vec3} d the fourth operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec3} out
+ */
+export function hermite(out, a, b, c, d, t) {
+  let factorTimes2 = t * t;
+  let factor1 = factorTimes2 * (2 * t - 3) + 1;
+  let factor2 = factorTimes2 * (t - 2) + t;
+  let factor3 = factorTimes2 * (t - 1);
+  let factor4 = factorTimes2 * (3 - 2 * t);
+
+  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
+  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
+  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
+
+  return out;
+}
+
+/**
+ * Performs a bezier interpolation with two control points
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the first operand
+ * @param {vec3} b the second operand
+ * @param {vec3} c the third operand
+ * @param {vec3} d the fourth operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec3} out
+ */
+export function bezier(out, a, b, c, d, t) {
+  let inverseFactor = 1 - t;
+  let inverseFactorTimesTwo = inverseFactor * inverseFactor;
+  let factorTimes2 = t * t;
+  let factor1 = inverseFactorTimesTwo * inverseFactor;
+  let factor2 = 3 * t * inverseFactorTimesTwo;
+  let factor3 = 3 * factorTimes2 * inverseFactor;
+  let factor4 = factorTimes2 * t;
+
+  out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
+  out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
+  out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
+
+  return out;
+}
+
+/**
+ * Generates a random vector with the given scale
+ *
+ * @param {vec3} out the receiving vector
+ * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+ * @returns {vec3} out
+ */
+export function random(out, scale) {
+  scale = scale || 1.0;
+
+  let r = glMatrix.RANDOM() * 2.0 * Math.PI;
+  let z = (glMatrix.RANDOM() * 2.0) - 1.0;
+  let zScale = Math.sqrt(1.0-z*z) * scale;
+
+  out[0] = Math.cos(r) * zScale;
+  out[1] = Math.sin(r) * zScale;
+  out[2] = z * scale;
+  return out;
+}
+
+/**
+ * Transforms the vec3 with a mat4.
+ * 4th vector component is implicitly '1'
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the vector to transform
+ * @param {mat4} m matrix to transform with
+ * @returns {vec3} out
+ */
+export function transformMat4(out, a, m) {
+  let x = a[0], y = a[1], z = a[2];
+  let w = m[3] * x + m[7] * y + m[11] * z + m[15];
+  w = w || 1.0;
+  out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
+  out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
+  out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
+  return out;
+}
+
+/**
+ * Transforms the vec3 with a mat3.
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the vector to transform
+ * @param {mat3} m the 3x3 matrix to transform with
+ * @returns {vec3} out
+ */
+export function transformMat3(out, a, m) {
+  let x = a[0], y = a[1], z = a[2];
+  out[0] = x * m[0] + y * m[3] + z * m[6];
+  out[1] = x * m[1] + y * m[4] + z * m[7];
+  out[2] = x * m[2] + y * m[5] + z * m[8];
+  return out;
+}
+
+/**
+ * Transforms the vec3 with a quat
+ * Can also be used for dual quaternions. (Multiply it with the real part)
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec3} a the vector to transform
+ * @param {quat} q quaternion to transform with
+ * @returns {vec3} out
+ */
+export function transformQuat(out, a, q) {
+    // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed
+    let qx = q[0], qy = q[1], qz = q[2], qw = q[3];
+    let x = a[0], y = a[1], z = a[2];
+    // var qvec = [qx, qy, qz];
+    // var uv = vec3.cross([], qvec, a);
+    let uvx = qy * z - qz * y,
+        uvy = qz * x - qx * z,
+        uvz = qx * y - qy * x;
+    // var uuv = vec3.cross([], qvec, uv);
+    let uuvx = qy * uvz - qz * uvy,
+        uuvy = qz * uvx - qx * uvz,
+        uuvz = qx * uvy - qy * uvx;
+    // vec3.scale(uv, uv, 2 * w);
+    let w2 = qw * 2;
+    uvx *= w2;
+    uvy *= w2;
+    uvz *= w2;
+    // vec3.scale(uuv, uuv, 2);
+    uuvx *= 2;
+    uuvy *= 2;
+    uuvz *= 2;
+    // return vec3.add(out, a, vec3.add(out, uv, uuv));
+    out[0] = x + uvx + uuvx;
+    out[1] = y + uvy + uuvy;
+    out[2] = z + uvz + uuvz;
+    return out;
+}
+
+/**
+ * Rotate a 3D vector around the x-axis
+ * @param {vec3} out The receiving vec3
+ * @param {vec3} a The vec3 point to rotate
+ * @param {vec3} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @returns {vec3} out
+ */
+export function rotateX(out, a, b, c){
+  let p = [], r=[];
+  //Translate point to the origin
+  p[0] = a[0] - b[0];
+  p[1] = a[1] - b[1];
+  p[2] = a[2] - b[2];
+
+  //perform rotation
+  r[0] = p[0];
+  r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c);
+  r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c);
+
+  //translate to correct position
+  out[0] = r[0] + b[0];
+  out[1] = r[1] + b[1];
+  out[2] = r[2] + b[2];
+
+  return out;
+}
+
+/**
+ * Rotate a 3D vector around the y-axis
+ * @param {vec3} out The receiving vec3
+ * @param {vec3} a The vec3 point to rotate
+ * @param {vec3} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @returns {vec3} out
+ */
+export function rotateY(out, a, b, c){
+  let p = [], r=[];
+  //Translate point to the origin
+  p[0] = a[0] - b[0];
+  p[1] = a[1] - b[1];
+  p[2] = a[2] - b[2];
+
+  //perform rotation
+  r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c);
+  r[1] = p[1];
+  r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c);
+
+  //translate to correct position
+  out[0] = r[0] + b[0];
+  out[1] = r[1] + b[1];
+  out[2] = r[2] + b[2];
+
+  return out;
+}
+
+/**
+ * Rotate a 3D vector around the z-axis
+ * @param {vec3} out The receiving vec3
+ * @param {vec3} a The vec3 point to rotate
+ * @param {vec3} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @returns {vec3} out
+ */
+export function rotateZ(out, a, b, c){
+  let p = [], r=[];
+  //Translate point to the origin
+  p[0] = a[0] - b[0];
+  p[1] = a[1] - b[1];
+  p[2] = a[2] - b[2];
+
+  //perform rotation
+  r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c);
+  r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c);
+  r[2] = p[2];
+
+  //translate to correct position
+  out[0] = r[0] + b[0];
+  out[1] = r[1] + b[1];
+  out[2] = r[2] + b[2];
+
+  return out;
+}
+
+/**
+ * Get the angle between two 3D vectors
+ * @param {vec3} a The first operand
+ * @param {vec3} b The second operand
+ * @returns {Number} The angle in radians
+ */
+export function angle(a, b) {
+  let tempA = fromValues(a[0], a[1], a[2]);
+  let tempB = fromValues(b[0], b[1], b[2]);
+
+  normalize(tempA, tempA);
+  normalize(tempB, tempB);
+
+  let cosine = dot(tempA, tempB);
+
+  if(cosine > 1.0) {
+    return 0;
+  }
+  else if(cosine < -1.0) {
+    return Math.PI;
+  } else {
+    return Math.acos(cosine);
+  }
+}
+
+/**
+ * Returns a string representation of a vector
+ *
+ * @param {vec3} a vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+export function str(a) {
+  return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')';
+}
+
+/**
+ * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {vec3} a The first vector.
+ * @param {vec3} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
+}
+
+/**
+ * Returns whether or not the vectors have approximately the same elements in the same position.
+ *
+ * @param {vec3} a The first vector.
+ * @param {vec3} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2];
+  let b0 = b[0], b1 = b[1], b2 = b[2];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)));
+}
+
+/**
+ * Alias for {@link vec3.subtract}
+ * @function
+ */
+export const sub = subtract;
+
+/**
+ * Alias for {@link vec3.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link vec3.divide}
+ * @function
+ */
+export const div = divide;
+
+/**
+ * Alias for {@link vec3.distance}
+ * @function
+ */
+export const dist = distance;
+
+/**
+ * Alias for {@link vec3.squaredDistance}
+ * @function
+ */
+export const sqrDist = squaredDistance;
+
+/**
+ * Alias for {@link vec3.length}
+ * @function
+ */
+export const len = length;
+
+/**
+ * Alias for {@link vec3.squaredLength}
+ * @function
+ */
+export const sqrLen = squaredLength;
+
+/**
+ * Perform some operation over an array of vec3s.
+ *
+ * @param {Array} a the array of vectors to iterate over
+ * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
+ * @param {Number} offset Number of elements to skip at the beginning of the array
+ * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
+ * @param {Function} fn Function to call for each vector in the array
+ * @param {Object} [arg] additional argument to pass to fn
+ * @returns {Array} a
+ * @function
+ */
+export const forEach = (function() {
+  let vec = create();
+
+  return function(a, stride, offset, count, fn, arg) {
+    let i, l;
+    if(!stride) {
+      stride = 3;
+    }
+
+    if(!offset) {
+      offset = 0;
+    }
+
+    if(count) {
+      l = Math.min((count * stride) + offset, a.length);
+    } else {
+      l = a.length;
+    }
+
+    for(i = offset; i < l; i += stride) {
+      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2];
+      fn(vec, vec, arg);
+      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2];
+    }
+
+    return a;
+  };
+})();
diff --git a/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec4.js b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec4.js
new file mode 100644
index 0000000..1b5da86
--- /dev/null
+++ b/third_party/webxr_test_pages/webxr-samples/js/third-party/gl-matrix/vec4.js
@@ -0,0 +1,606 @@
+/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+import * as glMatrix from "./common.js";
+
+/**
+ * 4 Dimensional Vector
+ * @module vec4
+ */
+
+/**
+ * Creates a new, empty vec4
+ *
+ * @returns {vec4} a new 4D vector
+ */
+export function create() {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = 0;
+  out[1] = 0;
+  out[2] = 0;
+  out[3] = 0;
+  return out;
+}
+
+/**
+ * Creates a new vec4 initialized with values from an existing vector
+ *
+ * @param {vec4} a vector to clone
+ * @returns {vec4} a new 4D vector
+ */
+export function clone(a) {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Creates a new vec4 initialized with the given values
+ *
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @param {Number} w W component
+ * @returns {vec4} a new 4D vector
+ */
+export function fromValues(x, y, z, w) {
+  let out = new glMatrix.ARRAY_TYPE(4);
+  out[0] = x;
+  out[1] = y;
+  out[2] = z;
+  out[3] = w;
+  return out;
+}
+
+/**
+ * Copy the values from one vec4 to another
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the source vector
+ * @returns {vec4} out
+ */
+export function copy(out, a) {
+  out[0] = a[0];
+  out[1] = a[1];
+  out[2] = a[2];
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Set the components of a vec4 to the given values
+ *
+ * @param {vec4} out the receiving vector
+ * @param {Number} x X component
+ * @param {Number} y Y component
+ * @param {Number} z Z component
+ * @param {Number} w W component
+ * @returns {vec4} out
+ */
+export function set(out, x, y, z, w) {
+  out[0] = x;
+  out[1] = y;
+  out[2] = z;
+  out[3] = w;
+  return out;
+}
+
+/**
+ * Adds two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function add(out, a, b) {
+  out[0] = a[0] + b[0];
+  out[1] = a[1] + b[1];
+  out[2] = a[2] + b[2];
+  out[3] = a[3] + b[3];
+  return out;
+}
+
+/**
+ * Subtracts vector b from vector a
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function subtract(out, a, b) {
+  out[0] = a[0] - b[0];
+  out[1] = a[1] - b[1];
+  out[2] = a[2] - b[2];
+  out[3] = a[3] - b[3];
+  return out;
+}
+
+/**
+ * Multiplies two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function multiply(out, a, b) {
+  out[0] = a[0] * b[0];
+  out[1] = a[1] * b[1];
+  out[2] = a[2] * b[2];
+  out[3] = a[3] * b[3];
+  return out;
+}
+
+/**
+ * Divides two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function divide(out, a, b) {
+  out[0] = a[0] / b[0];
+  out[1] = a[1] / b[1];
+  out[2] = a[2] / b[2];
+  out[3] = a[3] / b[3];
+  return out;
+}
+
+/**
+ * Math.ceil the components of a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to ceil
+ * @returns {vec4} out
+ */
+export function ceil(out, a) {
+  out[0] = Math.ceil(a[0]);
+  out[1] = Math.ceil(a[1]);
+  out[2] = Math.ceil(a[2]);
+  out[3] = Math.ceil(a[3]);
+  return out;
+}
+
+/**
+ * Math.floor the components of a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to floor
+ * @returns {vec4} out
+ */
+export function floor(out, a) {
+  out[0] = Math.floor(a[0]);
+  out[1] = Math.floor(a[1]);
+  out[2] = Math.floor(a[2]);
+  out[3] = Math.floor(a[3]);
+  return out;
+}
+
+/**
+ * Returns the minimum of two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function min(out, a, b) {
+  out[0] = Math.min(a[0], b[0]);
+  out[1] = Math.min(a[1], b[1]);
+  out[2] = Math.min(a[2], b[2]);
+  out[3] = Math.min(a[3], b[3]);
+  return out;
+}
+
+/**
+ * Returns the maximum of two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {vec4} out
+ */
+export function max(out, a, b) {
+  out[0] = Math.max(a[0], b[0]);
+  out[1] = Math.max(a[1], b[1]);
+  out[2] = Math.max(a[2], b[2]);
+  out[3] = Math.max(a[3], b[3]);
+  return out;
+}
+
+/**
+ * Math.round the components of a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to round
+ * @returns {vec4} out
+ */
+export function round(out, a) {
+  out[0] = Math.round(a[0]);
+  out[1] = Math.round(a[1]);
+  out[2] = Math.round(a[2]);
+  out[3] = Math.round(a[3]);
+  return out;
+}
+
+/**
+ * Scales a vec4 by a scalar number
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the vector to scale
+ * @param {Number} b amount to scale the vector by
+ * @returns {vec4} out
+ */
+export function scale(out, a, b) {
+  out[0] = a[0] * b;
+  out[1] = a[1] * b;
+  out[2] = a[2] * b;
+  out[3] = a[3] * b;
+  return out;
+}
+
+/**
+ * Adds two vec4's after scaling the second operand by a scalar value
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @param {Number} scale the amount to scale b by before adding
+ * @returns {vec4} out
+ */
+export function scaleAndAdd(out, a, b, scale) {
+  out[0] = a[0] + (b[0] * scale);
+  out[1] = a[1] + (b[1] * scale);
+  out[2] = a[2] + (b[2] * scale);
+  out[3] = a[3] + (b[3] * scale);
+  return out;
+}
+
+/**
+ * Calculates the euclidian distance between two vec4's
+ *
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {Number} distance between a and b
+ */
+export function distance(a, b) {
+  let x = b[0] - a[0];
+  let y = b[1] - a[1];
+  let z = b[2] - a[2];
+  let w = b[3] - a[3];
+  return Math.sqrt(x*x + y*y + z*z + w*w);
+}
+
+/**
+ * Calculates the squared euclidian distance between two vec4's
+ *
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {Number} squared distance between a and b
+ */
+export function squaredDistance(a, b) {
+  let x = b[0] - a[0];
+  let y = b[1] - a[1];
+  let z = b[2] - a[2];
+  let w = b[3] - a[3];
+  return x*x + y*y + z*z + w*w;
+}
+
+/**
+ * Calculates the length of a vec4
+ *
+ * @param {vec4} a vector to calculate length of
+ * @returns {Number} length of a
+ */
+export function length(a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  let w = a[3];
+  return Math.sqrt(x*x + y*y + z*z + w*w);
+}
+
+/**
+ * Calculates the squared length of a vec4
+ *
+ * @param {vec4} a vector to calculate squared length of
+ * @returns {Number} squared length of a
+ */
+export function squaredLength(a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  let w = a[3];
+  return x*x + y*y + z*z + w*w;
+}
+
+/**
+ * Negates the components of a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to negate
+ * @returns {vec4} out
+ */
+export function negate(out, a) {
+  out[0] = -a[0];
+  out[1] = -a[1];
+  out[2] = -a[2];
+  out[3] = -a[3];
+  return out;
+}
+
+/**
+ * Returns the inverse of the components of a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to invert
+ * @returns {vec4} out
+ */
+export function inverse(out, a) {
+  out[0] = 1.0 / a[0];
+  out[1] = 1.0 / a[1];
+  out[2] = 1.0 / a[2];
+  out[3] = 1.0 / a[3];
+  return out;
+}
+
+/**
+ * Normalize a vec4
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a vector to normalize
+ * @returns {vec4} out
+ */
+export function normalize(out, a) {
+  let x = a[0];
+  let y = a[1];
+  let z = a[2];
+  let w = a[3];
+  let len = x*x + y*y + z*z + w*w;
+  if (len > 0) {
+    len = 1 / Math.sqrt(len);
+    out[0] = x * len;
+    out[1] = y * len;
+    out[2] = z * len;
+    out[3] = w * len;
+  }
+  return out;
+}
+
+/**
+ * Calculates the dot product of two vec4's
+ *
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @returns {Number} dot product of a and b
+ */
+export function dot(a, b) {
+  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
+}
+
+/**
+ * Performs a linear interpolation between two vec4's
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the first operand
+ * @param {vec4} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec4} out
+ */
+export function lerp(out, a, b, t) {
+  let ax = a[0];
+  let ay = a[1];
+  let az = a[2];
+  let aw = a[3];
+  out[0] = ax + t * (b[0] - ax);
+  out[1] = ay + t * (b[1] - ay);
+  out[2] = az + t * (b[2] - az);
+  out[3] = aw + t * (b[3] - aw);
+  return out;
+}
+
+/**
+ * Generates a random vector with the given scale
+ *
+ * @param {vec4} out the receiving vector
+ * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+ * @returns {vec4} out
+ */
+export function random(out, vectorScale) {
+  vectorScale = vectorScale || 1.0;
+
+  //TODO: This is a pretty awful way of doing this. Find something better.
+  out[0] = glMatrix.RANDOM();
+  out[1] = glMatrix.RANDOM();
+  out[2] = glMatrix.RANDOM();
+  out[3] = glMatrix.RANDOM();
+  normalize(out, out);
+  scale(out, out, vectorScale);
+  return out;
+}
+
+/**
+ * Transforms the vec4 with a mat4.
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the vector to transform
+ * @param {mat4} m matrix to transform with
+ * @returns {vec4} out
+ */
+export function transformMat4(out, a, m) {
+  let x = a[0], y = a[1], z = a[2], w = a[3];
+  out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+  out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+  out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+  out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
+  return out;
+}
+
+/**
+ * Transforms the vec4 with a quat
+ *
+ * @param {vec4} out the receiving vector
+ * @param {vec4} a the vector to transform
+ * @param {quat} q quaternion to transform with
+ * @returns {vec4} out
+ */
+export function transformQuat(out, a, q) {
+  let x = a[0], y = a[1], z = a[2];
+  let qx = q[0], qy = q[1], qz = q[2], qw = q[3];
+
+  // calculate quat * vec
+  let ix = qw * x + qy * z - qz * y;
+  let iy = qw * y + qz * x - qx * z;
+  let iz = qw * z + qx * y - qy * x;
+  let iw = -qx * x - qy * y - qz * z;
+
+  // calculate result * inverse quat
+  out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
+  out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
+  out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+  out[3] = a[3];
+  return out;
+}
+
+/**
+ * Returns a string representation of a vector
+ *
+ * @param {vec4} a vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+export function str(a) {
+  return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
+}
+
+/**
+ * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
+ *
+ * @param {vec4} a The first vector.
+ * @param {vec4} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function exactEquals(a, b) {
+  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
+}
+
+/**
+ * Returns whether or not the vectors have approximately the same elements in the same position.
+ *
+ * @param {vec4} a The first vector.
+ * @param {vec4} b The second vector.
+ * @returns {Boolean} True if the vectors are equal, false otherwise.
+ */
+export function equals(a, b) {
+  let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
+  let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
+  return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
+          Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
+          Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
+          Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)));
+}
+
+/**
+ * Alias for {@link vec4.subtract}
+ * @function
+ */
+export const sub = subtract;
+
+/**
+ * Alias for {@link vec4.multiply}
+ * @function
+ */
+export const mul = multiply;
+
+/**
+ * Alias for {@link vec4.divide}
+ * @function
+ */
+export const div = divide;
+
+/**
+ * Alias for {@link vec4.distance}
+ * @function
+ */
+export const dist = distance;
+
+/**
+ * Alias for {@link vec4.squaredDistance}
+ * @function
+ */
+export const sqrDist = squaredDistance;
+
+/**
+ * Alias for {@link vec4.length}
+ * @function
+ */
+export const len = length;
+
+/**
+ * Alias for {@link vec4.squaredLength}
+ * @function
+ */
+export const sqrLen = squaredLength;
+
+/**
+ * Perform some operation over an array of vec4s.
+ *
+ * @param {Array} a the array of vectors to iterate over
+ * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed
+ * @param {Number} offset Number of elements to skip at the beginning of the array
+ * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array
+ * @param {Function} fn Function to call for each vector in the array
+ * @param {Object} [arg] additional argument to pass to fn
+ * @returns {Array} a
+ * @function
+ */
+export const forEach = (function() {
+  let vec = create();
+
+  return function(a, stride, offset, count, fn, arg) {
+    let i, l;
+    if(!stride) {
+      stride = 4;
+    }
+
+    if(!offset) {
+      offset = 0;
+    }
+
+    if(count) {
+      l = Math.min((count * stride) + offset, a.length);
+    } else {
+      l = a.length;
+    }
+
+    for(i = offset; i < l; i += stride) {
+      vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3];
+      fn(vec, vec, arg);
+      a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3];
+    }
+
+    return a;
+  };
+})();
diff --git a/third_party/webxr_test_pages/webxr-samples/js/webxr-button.js b/third_party/webxr_test_pages/webxr-samples/js/webxr-button.js
index 1028a5b9..1a4dcaa4 100644
--- a/third_party/webxr_test_pages/webxr-samples/js/webxr-button.js
+++ b/third_party/webxr_test_pages/webxr-samples/js/webxr-button.js
@@ -14,7 +14,7 @@
 
 // This is a stripped down and specialized version of WebVR-UI
 // (https://github.com/googlevr/webvr-ui) that takes out most of the state
-// management in favor of providing a simple way of listing available devices
+// management in favor of providing a simple way of requesting entry into WebXR
 // for the needs of the sample pages. Functionality like beginning sessions
 // is intentionally left out so that the sample pages can demonstrate them more
 // clearly.
@@ -309,7 +309,7 @@
 
     this.options = options;
 
-    this.device = null;
+    this._enabled = false;
     this.session = null;
 
     // Pass in your own domElement if you really dont want to use ours
@@ -325,17 +325,24 @@
   }
 
   /**
-   * Sets the XRDevice this button is associated with.
-   * @param {XRDevice} device
-   * @return {EnterXRButton}
+   * Sets the enabled state of this button.
+   * @param {boolean} enabled
    */
-  setDevice(device) {
-    this.device = device;
+  set enabled(enabled) {
+    this._enabled = enabled;
     this.__updateButtonState();
     return this;
   }
 
   /**
+   * Gets the enabled state of this button.
+   * @return {boolean}
+   */
+  get enabled() {
+    return this._enabled;
+  }
+
+  /**
    * Indicate that there's an active XRSession. Switches the button to "Exit XR"
    * state if not null, or "Enter XR" state if null.
    * @param {XRSession} session
@@ -443,8 +450,8 @@
   __onXRButtonClick() {
     if (this.session) {
       this.options.onEndSession(this.session);
-    } else if (this.device) {
-      this.options.onRequestSession(this.device);
+    } else if (this._enabled) {
+      this.options.onRequestSession();
     }
   }
 
@@ -457,7 +464,7 @@
       this.setTitle(this.options.textExitXRTitle);
       this.setTooltip('Exit XR presentation');
       this.__setDisabledAttribute(false);
-    } else if (this.device) {
+    } else if (this._enabled) {
       this.setTitle(this.options.textEnterXRTitle);
       this.setTooltip('Enter XR');
       this.__setDisabledAttribute(false);
diff --git a/third_party/webxr_test_pages/webxr-samples/js/webxr-version-shim.js b/third_party/webxr_test_pages/webxr-samples/js/webxr-version-shim.js
deleted file mode 100644
index 359fd08..0000000
--- a/third_party/webxr_test_pages/webxr-samples/js/webxr-version-shim.js
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2018 The Immersive Web Community Group
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-// This file contains various patches to adjust for differences in outdated browser
-// implementations of the WebXR API and allow the samples to be coded exclusively
-// against the most recent version.
-
-class XRRayShim {
-  constructor(rayMatrix) {
-    this._transformMatrix = rayMatrix;
-
-    // TODO: Don't rely on these types for the shim.
-    // Some browsers don't support them yet.
-    let o = new DOMPointReadOnly(0, 0, 0, 1);
-    let d = new DOMPointReadOnly(0, 0, -1, 0);
-    let t = new DOMMatrix(rayMatrix);
-
-    this._origin = DOMPointReadOnly.fromPoint(t.transformPoint(o));
-    this._direction = DOMPointReadOnly.fromPoint(t.transformPoint(d));
-  }
-
-  get origin() {
-    return this._origin;
-  }
-
-  get direction() {
-    return this._direction;
-  }
-
-  get transformMatrix() {
-    return this._transformMatrix;
-  }
-}
-
-class WebXRVersionShim {
-  constructor() {
-    if (this._shouldApplyPatch()) {
-      this._applyPatch();
-    }
-  }
-
-  _isMobile() {
-    return /Android/i.test(navigator.userAgent) ||
-          /iPhone|iPad|iPod/i.test(navigator.userAgent);s
-  }
-
-  _shouldApplyPatch() {
-    // Don't apply the patch with WebXR isn't available.
-    if (!('xr' in navigator)) {
-      return false;
-    }
-
-    // Allow for universally disabling the version shim with a URL arg, which will
-    // make it easier to test updates to native implementations.
-    let query = window.location.search.substring(1) || window.location.hash.substring(1);
-    let vars = query.split('&');
-    for (let i = 0; i < vars.length; i++) {
-      let pair = vars[i].split('=');
-      if (pair[0].toLowerCase() == 'nowebxrversionshim') {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  _applyPatch() {
-    //===========================
-    // Chrome 67/68
-    //===========================
-
-    // Map 'immersive' to the old 'exclusive' verbiage if needed.
-    if ('exclusive' in XRSession.prototype) {
-      const NATIVE_SUPPORTS_SESSION = XRDevice.prototype.supportsSession;
-      XRDevice.prototype.supportsSession = function(options) {
-        options.exclusive = !!options.immersive;
-        return NATIVE_SUPPORTS_SESSION.call(this, options);
-      };
-
-      const NATIVE_REQUEST_SESSION = XRDevice.prototype.requestSession;
-      XRDevice.prototype.requestSession = function(options) {
-        options.exclusive = !!options.immersive;
-        return NATIVE_REQUEST_SESSION.call(this, options);
-      };
-
-      Object.defineProperty(XRSession.prototype, 'immersive', {
-        enumerable: true, configurable: false, writeable: false,
-        get: function() { return this.exclusive; }
-      });
-    }
-
-    // We can't test for the existence of the enums in question directly, so this code
-    // will just try to create the requested type and fall back if it fails.
-    const NATIVE_REQUEST_FRAME_OF_REFERENCE = XRSession.prototype.requestReferenceSpace;
-    XRSession.prototype.requestFrameOfReference = function(type, options_original) {
-      let session = this;
-      let options = options_original || {};
-      options["type"] = "stationary";
-      options["subtype"] = type;
-      // Try the current type.
-      return NATIVE_REQUEST_FRAME_OF_REFERENCE.call(session, options).catch((error)=>{
-        // FIXME: Should be checking for TypeError specifically. Requires a polyfill update.
-        if(error instanceof Error) {
-          // If the current type fails, switch to the other version.
-          switch(type) {
-            case 'eye-level':
-              options["subtype"] = 'eyeLevel';
-              break;
-            case 'head-model':
-              options["subtype"] = 'headModel';
-              break;
-            default:
-              return Promise.reject(error);
-          }
-          return Promise.resolve(NATIVE_REQUEST_FRAME_OF_REFERENCE.call(session, options));
-        } else {
-          return Promise.reject(error);
-        }
-      });
-    };
-
-    // Make sure that requestAnimationFrame is always supplied with a timestamp
-    const NATIVE_REQUEST_ANIMATION_FRAME = XRSession.prototype.requestAnimationFrame;
-    XRSession.prototype.requestAnimationFrame = function(callback) {
-      return NATIVE_REQUEST_ANIMATION_FRAME.call(this, (timestamp, frame) => {
-        callback(timestamp ? timestamp : performance.now(), frame);
-      });
-    };
-
-    if (!('getNativeFramebufferScaleFactor' in XRWebGLLayer)) {
-      if (this._isMobile()) {
-        const NATIVE_WEBGL_LAYER = XRWebGLLayer;
-        const NATIVE_WEBGL_LAYER_PROTOTYPE = XRWebGLLayer.prototype;
-        XRWebGLLayer = function(session, gl, options) {
-          if (options && options.framebufferScaleFactor) {
-            // On Chrome 67/68 mobile the default framebuffer returned is 0.7 of full res.
-            options.framebufferScaleFactor = 0.7 * options.framebufferScaleFactor;
-          }
-
-          return new NATIVE_WEBGL_LAYER(session, gl, options);
-        };
-
-        XRWebGLLayer.prototype = NATIVE_WEBGL_LAYER_PROTOTYPE;
-
-        Object.defineProperty(XRWebGLLayer, 'getNativeFramebufferScaleFactor', {
-          enumerable: true, configurable: false, writeable: false,
-          value: function(session) {
-            if (!session) {
-              throw new TypeError('No XRSession specified');
-            }
-            if (isMobile()) {
-              // On Chrome 67/68 mobile the default framebuffer returned is 0.7 of full res.
-              return 1.42857;
-            } else {
-              // On Chrome 67/68 desktop the full res buffer is already returned.
-              return 1.0;
-            }
-          }
-        });
-      }
-    }
-
-    // If the environmentBlendMode isn't available report it as 'opaque', since any
-    // implementations lacking this property only really worked on VR headsets.
-    if (!('environmentBlendMode' in XRSession.prototype)) {
-      Object.defineProperty(XRSession.prototype, 'environmentBlendMode', {
-        enumerable: true, configurable: false, writeable: false,
-        get: function() { return 'opaque'; }
-      });
-    }
-
-    if (!('targetRayMode' in XRInputSource.prototype)) {
-      Object.defineProperty(XRInputSource.prototype, 'targetRayMode', {
-        enumerable: true, configurable: false, writeable: false,
-        get: function() {
-          switch (this.pointerOrigin) {
-            case 'head': return 'gaze';
-            case 'hand': return 'tracked-pointer';
-            case 'screen': return 'screen';
-
-            default: throw new ValueError('Unrecognized pointerOrigin: ' + this.pointerOrigin);
-          }
-        }
-      });
-    }
-
-    if (!('targetRay' in XRInputPose.prototype)) {
-      Object.defineProperty(XRInputPose.prototype, 'targetRay', {
-        enumerable: true, configurable: false, writeable: false,
-        get: function() {
-          if (!this._targetRay) {
-            if (this.targetRayMatrix) {
-              this._targetRay = new XRRayShim(this.targetRayMatrix);
-            } else if (this.pointerMatrix) {
-              this._targetRay = new XRRayShim(this.pointerMatrix);
-            }
-          }
-          return this._targetRay || null;
-        }
-      });
-    }
-  }
-}
diff --git a/third_party/webxr_test_pages/webxr-samples/magic-window.html b/third_party/webxr_test_pages/webxr-samples/magic-window.html
index 3baa4c9..e9a01bf7 100644
--- a/third_party/webxr_test_pages/webxr-samples/magic-window.html
+++ b/third_party/webxr_test_pages/webxr-samples/magic-window.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -59,25 +52,25 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // Shh! Nothing to see here, move along.
       let arMode = QueryArgs.getBool('arMode', false);
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -100,38 +93,36 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            // In order for a non-immersive session to be used we must provide
-            // an outputContext, which indicates the canvas that will contain
-            // results of the session's rendering.
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            // Pick an arbitrary device for the magic window content and start
-            // up a non-immersive session if possible.
-            device.requestSession({outputContext: ctx})
-                .then((session) => {
-                  // Add the canvas to the document once we know that it will be
-                  // rendered to.
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          // In order for a non-immersive session to be used we must provide
+          // an outputContext, which indicates the canvas that will contain
+          // results of the session's rendering.
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          // Pick an arbitrary device for the magic window content and start
+          // up a non-immersive session if possible.
+          navigator.xr.requestSession({outputContext: ctx})
+              .then((session) => {
+                // Add the canvas to the document once we know that it will be
+                // rendered to.
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         }
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({immersive: true, outputContext: ctx}).then((session) => {
+        navigator.xr.requestSession({mode: 'immersive-vr', outputContext: ctx}).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -142,7 +133,7 @@
 
         if (!gl) {
           gl = createWebGLContext({
-            compatibleXRDevice: session.device
+            xrCompatible: true
           });
 
           renderer = new Renderer(gl);
@@ -152,13 +143,13 @@
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
           // Since we're dealing with multple sessions now we need to track
-          // which XRFrameOfReference is associated with which XRSession.
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+          // which XRReferenceSpace is associated with which XRSession.
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -170,7 +161,7 @@
 
       function onSessionEnded(event) {
         // Only reset the button when the immersive session ends.
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           // Remove the mirroring canvas.
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
@@ -181,10 +172,10 @@
       function onXRFrame(t, frame) {
         let session = frame.session;
         // Ensure that we're using the right frame of reference for the session.
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -196,12 +187,12 @@
             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
           }
 
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             let viewport = session.baseLayer.getViewport(view);
             gl.viewport(viewport.x, viewport.y,
                         viewport.width, viewport.height);
 
-            scene.draw(view.projectionMatrix, pose.getViewMatrix(view));
+            scene.draw(view.projectionMatrix, view.viewMatrix);
           }
         }
 
@@ -210,7 +201,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/mirroring.html b/third_party/webxr_test_pages/webxr-samples/mirroring.html
index d552693..9c576eb 100644
--- a/third_party/webxr_test_pages/webxr-samples/mirroring.html
+++ b/third_party/webxr_test_pages/webxr-samples/mirroring.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -63,21 +56,21 @@
     <main style='text-align: center;'>
       <p>Click 'Enter XR' to see content</p>
     </main>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrFrameOfRef = null;
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -94,15 +87,13 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
         }
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // In order to mirror an exclusive session, we must provide
         // an XRPresentationContext, which indicates the canvas that will
         // contain results of the session's rendering.
@@ -115,8 +106,8 @@
 
         // Providing an outputContext when requesting an exclusive session
         // indicates that it should be used as the mirror destination.
-        device.requestSession({
-          immersive: true,
+        navigator.xr.requestSession({
+          mode: 'immersive-vr',
           outputContext: ctx
         }).then(onSessionStarted);
       }
@@ -128,7 +119,7 @@
 
         if (!gl) {
           gl = createWebGLContext({
-            compatibleXRDevice: session.device
+            xrCompatible: true
           });
 
           renderer = new Renderer(gl);
@@ -144,8 +135,8 @@
         outputCanvas.width = session.baseLayer.framebufferWidth / 2;
         outputCanvas.height = session.baseLayer.framebufferHeight;
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
 
           session.requestAnimationFrame(onXRFrame);
         });
@@ -171,18 +162,18 @@
 
         session.requestAnimationFrame(onXRFrame);
 
-        let pose = frame.getDevicePose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         if (pose) {
           gl.bindFramebuffer(gl.FRAMEBUFFER, session.baseLayer.framebuffer);
           gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             let viewport = session.baseLayer.getViewport(view);
             gl.viewport(viewport.x, viewport.y,
                         viewport.width, viewport.height);
 
-            scene.draw(view.projectionMatrix, pose.getViewMatrix(view));
+            scene.draw(view.projectionMatrix, view.viewMatrix);
           }
         }
 
@@ -192,7 +183,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/module-tester.html b/third_party/webxr_test_pages/webxr-samples/module-tester.html
index 5d3de9e..0952950 100644
--- a/third_party/webxr_test_pages/webxr-samples/module-tester.html
+++ b/third_party/webxr_test_pages/webxr-samples/module-tester.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <style>
       #scene-select {
         position: absolute;
@@ -86,6 +79,8 @@
       import {CubeSeaNode} from './js/cottontail/src/nodes/cube-sea.js';
       import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
 
+      import {mat4, vec3} from './js/cottontail/src/math/gl-matrix.js';
+
       const IDENTITY_MATRIX = new Float32Array([1, 0, 0, 0,
                                                 0, 1, 0, 0,
                                                 0, 0, 1, 0,
diff --git a/third_party/webxr_test_pages/webxr-samples/positional-audio.html b/third_party/webxr_test_pages/webxr-samples/positional-audio.html
index 0b9b72b..f67d78c 100644
--- a/third_party/webxr_test_pages/webxr-samples/positional-audio.html
+++ b/third_party/webxr_test_pages/webxr-samples/positional-audio.html
@@ -38,15 +38,8 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
     <script src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
 
-    <script src='js/cottontail/build/cottontail.debug.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -61,17 +54,19 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {UrlTexture} from './js/cottontail/src/core/texture.js';
+      import {mat4} from './js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // Temporary
       let hideStats = QueryArgs.getBool('hideStats', false);
@@ -81,8 +76,8 @@
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -273,55 +268,51 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            // Load multiple audio sources.
-            Promise.all([
-              createAudioSource({
-                url: 'media/sound/guitar.ogg',
-                position: [0, DEFAULT_HEIGHT, -1],
-                rotateY: 0
-              }),
-              createAudioSource({
-                url: 'media/sound/drums.ogg',
-                position: [-1, DEFAULT_HEIGHT, 0],
-                rotateY: Math.PI * 0.5
-              }),
-              createAudioSource({
-                url: 'media/sound/perc.ogg',
-                position: [1, DEFAULT_HEIGHT, 0],
-                rotateY: Math.PI * -0.5
-              }),
-            ]).then((sources) => {
-              audioSources = sources;
-
-              // Once the audio is loaded, create a button that toggles the
-              // audio state when clicked.
-              playButton = new ButtonNode(playTexture, () => {
-                if (audioContext.state == 'running') {
-                  pauseAudio();
-                } else {
-                  playAudio();
-                }
-              });
-              playButton.translation = [0, 1.2, -0.65];
-              scene.addNode(playButton);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          // Load multiple audio sources.
+          Promise.all([
+            createAudioSource({
+              url: 'media/sound/guitar.ogg',
+              position: [0, DEFAULT_HEIGHT, -1],
+              rotateY: 0
+            }),
+            createAudioSource({
+              url: 'media/sound/drums.ogg',
+              position: [-1, DEFAULT_HEIGHT, 0],
+              rotateY: Math.PI * 0.5
+            }),
+            createAudioSource({
+              url: 'media/sound/perc.ogg',
+              position: [1, DEFAULT_HEIGHT, 0],
+              rotateY: Math.PI * -0.5
+            }),
+          ]).then((sources) => {
+            audioSources = sources;
+
+            // Once the audio is loaded, create a button that toggles the
+            // audio state when clicked.
+            playButton = new ButtonNode(playTexture, () => {
+              if (audioContext.state == 'running') {
+                pauseAudio();
+              } else {
+                playAudio();
+              }
+            });
+            playButton.translation = [0, 1.2, -0.65];
+            scene.addNode(playButton);
+          });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -334,12 +325,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -347,14 +338,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -366,21 +357,21 @@
         session.addEventListener('selectstart', onSelectStart);
         session.addEventListener('selectend', onSelectEnd);
         session.addEventListener('select', (ev) => {
-          let frameOfRef = ev.frame.session.immersive ?
-                           xrImmersiveFrameOfRef :
-                           xrNonImmersiveFrameOfRef;
-          scene.handleSelect(ev.inputSource, ev.frame, frameOfRef);
+          let refSpace = ev.frame.session.mode.startsWith('immersive') ?
+                           xrImmersiveRefSpace :
+                           xrNonImmersiveRefSpace;
+          scene.handleSelect(ev.inputSource, ev.frame, refSpace);
         });
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -392,7 +383,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
 
@@ -405,8 +396,8 @@
       let draggingInput = null;
       let draggingTransform = mat4.create();
 
-      function hitTest(inputSource, frame, frameOfRef) {
-        let inputPose = frame.getInputPose(inputSource, frameOfRef);
+      function hitTest(inputSource, frame, refSpace) {
+        let inputPose = frame.getInputPose(inputSource, refSpace);
         if (!inputPose) {
           return;
         }
@@ -430,10 +421,10 @@
       }
 
       function onSelectStart(ev) {
-        let frameOfRef = ev.frame.session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        hitTest(ev.inputSource, ev.frame, frameOfRef);
+        let refSpace = ev.frame.session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        hitTest(ev.inputSource, ev.frame, refSpace);
       }
 
       function onSelectEnd(ev) {
@@ -444,19 +435,19 @@
       let tmpMatrix = mat4.create();
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         if (draggingSource) {
-          let draggingPose = frame.getInputPose(draggingInput, frameOfRef);
+          let draggingPose = frame.getInputPose(draggingInput, refSpace);
           if (draggingPose) {
             let pos = draggingSource.position;
             mat4.multiply(tmpMatrix, draggingPose.targetRay.transformMatrix, draggingTransform);
@@ -478,7 +469,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html
index 201501a..4121e17 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/cottontail/build/cottontail.js'></script>
-
     <script src='../js/webxr-button.js'></script>
   </head>
   <body>
@@ -61,27 +54,26 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
+      import {Node} from '../js/cottontail/src/core/node.js';
+      import {DropShadowNode} from '../js/cottontail/src/nodes/drop-shadow.js';
+      import {vec3} from '../js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       let useReticle = document.getElementById('useReticle');
 
       // XR globals.
       let xrButton = null;
-      let xrFrameOfRef = null;
-
-      let outputCanvas = document.createElement('canvas');
-      outputCanvas.setAttribute('id', 'output-canvas');
-      let ctx = outputCanvas.getContext('xrpresent');
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -119,23 +111,45 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({ environmentIntegration: true, outputContext: ctx }).then(() => {
-              xrButton.setDevice(device);
+          navigator.xr.supportsSessionMode('immersive-ar').then(() => {
+            xrButton.enabled = true;
+          }).catch(() => {
+            navigator.xr.supportsSessionMode('legacy-inline-ar').then(() => {
+                xrButton.enabled = true;
             });
           });
         }
       }
 
-      function onRequestSession(device) {
-        device.requestSession({ environmentIntegration: true, outputContext: ctx })
-            .then((session) => {
-              // Add the canvas to the document once we know that it will be
-              // rendered to.
-              document.body.appendChild(outputCanvas);
+      function makeCanvas() {
+          // Create a fullscreen canvas element for use with legacy AR mode.
+          let canvas = document.createElement('canvas');
+          canvas.style.width = '100%';
+          canvas.style.height = '100%';
+          canvas.style.left = 0;
+          canvas.style.top = 0;
+          canvas.style.right = 0;
+          canvas.style.bottom = 0;
+          canvas.style.margin = 0;
+          canvas.id = 'legacy-canvas';
+          return canvas;
+      }
+
+      function onRequestSession() {
+        navigator.xr.requestSession({ mode: 'immersive-ar' }).then((session) => {
               xrButton.setSession(session);
               onSessionStarted(session);
+        }).catch(() => {
+            let legacyCanvas = makeCanvas();
+            let legacyCtx = legacyCanvas.getContext('xrpresent');
+            navigator.xr.requestSession({ mode: 'legacy-inline-ar',
+                                          outputContext: legacyCtx
+                                        }).then((session) => {
+                document.body.appendChild(legacyCanvas);
+                xrButton.setSession(session);
+                onSessionStarted(session);
             });
+        });
       }
 
       function onSessionStarted(session) {
@@ -144,8 +158,7 @@
 
         if (!gl) {
           gl = createWebGLContext({
-            compatibleXRDevice: session.device,
-            xrCompatible : true,
+            xrCompatible: true
           });
 
           renderer = new Renderer(gl);
@@ -155,19 +168,20 @@
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
           session.requestAnimationFrame(onXRFrame);
         });
       }
 
       function onEndSession(session) {
         session.end();
-        // Remove the injected output canvas from the DOM.
-        document.body.removeChild(document.querySelector('#output-canvas'));
       }
 
       function onSessionEnded(event) {
+        if (event.session.mode.startsWith('legacy')) {
+          document.body.removeChild(document.querySelector('#legacy-canvas'));
+        }
         xrButton.setSession(null);
       }
 
@@ -201,7 +215,7 @@
         } else {
           // Otherwise we'll use the target ray from the input source that generated
           // this event to fire off a new hit test.
-          let inputPose = event.frame.getInputPose(event.inputSource, xrFrameOfRef);
+          let inputPose = event.frame.getInputPose(event.inputSource, xrRefSpace);
           if (!inputPose) {
             return;
           }
@@ -215,7 +229,7 @@
                 inputPose.targetRay.direction.x,
                 inputPose.targetRay.direction.y,
                 inputPose.targetRay.direction.z);
-            event.frame.session.requestHitTest(rayOrigin, rayDirection, xrFrameOfRef).then((results) => {
+            event.frame.session.requestHitTest(rayOrigin, rayDirection, xrRefSpace).then((results) => {
               if (results.length) {
                 addARObjectAt(results[0].hitMatrix);
               }
@@ -227,7 +241,7 @@
       // Called every time a XRSession requests that a new frame be drawn.
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let pose = frame.getViewerPose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         // If requested, use the pose to cast a reticle into the scene using a
         // continuous hit test. For the moment we're just using the flower
@@ -241,7 +255,7 @@
           vec3.sub(rayDirection, rayDirection, rayOrigin);
           vec3.normalize(rayDirection, rayDirection);
 
-          session.requestHitTest(rayOrigin, rayDirection, xrFrameOfRef).then((results) => {
+          session.requestHitTest(rayOrigin, rayDirection, xrRefSpace).then((results) => {
             // When the hit test returns use it to place our proxy object.
             if (results.length) {
               let hitResult = results[0];
@@ -266,7 +280,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html
index 69c502d..b32c139 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/cottontail/build/cottontail.js'></script>
-
     <script src='../js/webxr-button.js'></script>
   </head>
   <body>
@@ -58,27 +51,21 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrFrameOfRef = null;
-
-      // AR sessions must have an outputContext, just like magic window
-      // sessions.
-      let outputCanvas = document.createElement('canvas');
-      outputCanvas.setAttribute('id', 'output-canvas');
-      let ctx = outputCanvas.getContext('xrpresent');
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -105,17 +92,33 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            // Checks to ensure that environment integration (AR) is available,
-            // and only enables the button if so.
-            device.supportsSession({ environmentIntegration: true, outputContext: ctx }).then(() => {
-              xrButton.setDevice(device);
+          // Checks to ensure that environment integration (AR) is available,
+          // and only enables the button if so.
+          navigator.xr.supportsSessionMode('immersive-ar').then(() => {
+            xrButton.enabled = true;
+          }).catch(() => {
+            navigator.xr.supportsSessionMode('legacy-inline-ar').then(() => {
+                xrButton.enabled = true;
             });
           });
         }
       }
 
-      function onRequestSession(device) {
+      function makeCanvas() {
+          // Create a fullscreen canvas element for use with legacy AR mode.
+          let canvas = document.createElement('canvas');
+          canvas.style.width = '100%';
+          canvas.style.height = '100%';
+          canvas.style.left = 0;
+          canvas.style.top = 0;
+          canvas.style.right = 0;
+          canvas.style.bottom = 0;
+          canvas.style.margin = 0;
+          canvas.id = 'legacy-canvas';
+          return canvas;
+      }
+
+      function onRequestSession() {
         // Requests an inline (non-immersive) session with environment integration
         // to get AR via video passthrough.
 
@@ -124,14 +127,20 @@
         // activation event so that appropriate permissions can be granted.
         // This will likely prompt the user to allow camera use, so the promise
         // may remain outstanding for a while.
-        device.requestSession({ environmentIntegration: true, outputContext: ctx })
-            .then((session) => {
-              // Add the canvas to the document once we know that it will be
-              // rendered to.
-              document.body.appendChild(outputCanvas);
-              xrButton.setSession(session);
-              onSessionStarted(session);
+        navigator.xr.requestSession({ mode: 'immersive-ar' }).then((session) => {
+            xrButton.setSession(session);
+            onSessionStarted(session);
+        }).catch(() => {
+            let legacyCanvas = makeCanvas();
+            let legacyCtx = legacyCanvas.getContext('xrpresent');
+            navigator.xr.requestSession({ mode: 'legacy-inline-ar',
+                                          outputContext: legacyCtx
+                                        }).then((session) => {
+                document.body.appendChild(legacyCanvas);
+                xrButton.setSession(session);
+                onSessionStarted(session);
             });
+        });
       }
 
       function onSessionStarted(session) {
@@ -139,8 +148,7 @@
 
         if (!gl) {
           gl = createWebGLContext({
-            compatibleXRDevice: session.device,
-            xrCompatible : true,
+            xrCompatible: true
           });
 
           renderer = new Renderer(gl);
@@ -150,26 +158,27 @@
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
           session.requestAnimationFrame(onXRFrame);
         });
       }
 
       function onEndSession(session) {
         session.end();
-        // Remove the injected output canvas from the DOM.
-        document.body.removeChild(document.querySelector('#output-canvas'));
       }
 
       function onSessionEnded(event) {
+        if (event.session.mode.startsWith('legacy')) {
+          document.body.removeChild(document.querySelector('#legacy-canvas'));
+        }
         xrButton.setSession(null);
       }
 
       // Called every time a XRSession requests that a new frame be drawn.
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let pose = frame.getViewerPose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         scene.startFrame();
 
@@ -182,7 +191,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/reduced-bind-rendering.html b/third_party/webxr_test_pages/webxr-samples/reduced-bind-rendering.html
index 585e6df..72f2258 100644
--- a/third_party/webxr_test_pages/webxr-samples/reduced-bind-rendering.html
+++ b/third_party/webxr_test_pages/webxr-samples/reduced-bind-rendering.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -59,22 +52,22 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {WebXRView, Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {CubeSeaNode} from './js/cottontail/src/nodes/cube-sea.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -90,22 +83,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -122,12 +111,12 @@
         let fallbackHelper = new FallbackHelper(scene, gl);
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
         // Set up a non-black clear color so that we can see if something renders wrong.
         gl.clearColor(0.1, 0.2, 0.3, 1.0);
@@ -137,14 +126,14 @@
         scene.setRenderer(renderer);
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -153,15 +142,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -172,7 +161,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -180,10 +169,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -239,7 +228,7 @@
           // more efficient pattern anyway as a way of promoting best practices.
 
           let views = [];
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             // Gather all the values needed for one view and push it into the
             // array of views to be drawn. WebXRView is a utility class that
             // holds all the necessary values for drawing a single view.
@@ -249,7 +238,7 @@
             // scene.drawXRViews() function, which handles gathering these
             // values internally.
             renderView.projectionMatrix = view.projectionMatrix;
-            renderView.viewMatrix = pose.getViewMatrix(view);
+            renderView.viewMatrix = view.viewMatrix;
             renderView.viewport = session.baseLayer.getViewport(view);
             views.push(renderView);
           }
@@ -261,7 +250,6 @@
       }
 
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/room-scale.html b/third_party/webxr_test_pages/webxr-samples/room-scale.html
index 2096d5e..3c4c952 100644
--- a/third_party/webxr_test_pages/webxr-samples/room-scale.html
+++ b/third_party/webxr_test_pages/webxr-samples/room-scale.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -59,22 +52,23 @@
         </p>
       </summary>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -92,22 +86,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -120,25 +110,25 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -149,7 +139,7 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -159,14 +149,14 @@
         // coordinates (for example, with a 3DoF device) then it will return an
         // emulated stage, where the view is translated up by a static height so
         // that the scene still renders in approximately the right place.
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
             let boundsRenderer = new BoundsRenderer();
-            boundsRenderer.stageBounds = frameOfRef.bounds;
+            boundsRenderer.stageBounds = refSpace.bounds;
             scene.addNode(boundsRenderer);
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -178,7 +168,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -186,10 +176,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -206,7 +196,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/spectator-mode.html b/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
index 92fb937d..d28949d 100644
--- a/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
+++ b/third_party/webxr_test_pages/webxr-samples/spectator-mode.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -61,22 +54,23 @@
     </header>
     <main style='text-align: center;'>
     </main>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {mat4} from './js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let outputCanvas = null;
@@ -104,22 +98,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -132,12 +122,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -149,8 +139,8 @@
         window.addEventListener('resize', onResize);
       }
 
-      function onRequestSession(device) {
-        device.requestSession({ immersive: true }).then((session) => {
+      function onRequestSession() {
+        navigator.xr.requestSession({ mode: 'immersive-vr' }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
 
@@ -174,15 +164,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -194,7 +184,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           xrButton.setSession(null);
 
           // When we exit the exclusive session, stop presenting the spectator
@@ -250,16 +240,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -309,7 +299,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/stereo-video.html b/third_party/webxr_test_pages/webxr-samples/stereo-video.html
index be332d8..bd61ff5 100644
--- a/third_party/webxr_test_pages/webxr-samples/stereo-video.html
+++ b/third_party/webxr_test_pages/webxr-samples/stereo-video.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -52,27 +45,31 @@
       <details open>
         <summary>Stereo Video Player</summary>
         <p>
-          This sample demonstrates how to play a stereo 3D video. (Stereo part not yet working.)
+          This sample demonstrates how to play a stereo 3D video.
           <a class="back" href="./">Back</a>
         </p>
       </summary>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
+      import {UrlTexture} from './js/cottontail/src/core/texture.js';
+      import {ButtonNode} from './js/cottontail/src/nodes/button.js';
+      import {VideoNode} from './js/cottontail/src/nodes/video.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -141,22 +138,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -168,12 +161,12 @@
         let fallbackHelper = new FallbackHelper(scene, gl);
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -181,14 +174,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -197,13 +190,13 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
         session.addEventListener('select', (ev) => {
-          let frameOfRef = ev.frame.session.immersive ?
-                           xrImmersiveFrameOfRef :
-                           xrNonImmersiveFrameOfRef;
-          scene.handleSelect(ev.inputSource, ev.frame, frameOfRef);
+          let refSpace = ev.frame.session.mode.startsWith('immersive') ?
+                           xrImmersiveRefSpace :
+                           xrNonImmersiveRefSpace;
+          scene.handleSelect(ev.inputSource, ev.frame, refSpace);
         });
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
@@ -211,11 +204,11 @@
         // because we want to users head to appear in the right place relative
         // to the center chair, as if they're sitting in it, rather than
         // somewhere in the room relative to the floor.
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -227,7 +220,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -235,16 +228,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -253,7 +246,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/cube-sea.html b/third_party/webxr_test_pages/webxr-samples/tests/cube-sea.html
index e1c40e9..c047eefe 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/cube-sea.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/cube-sea.html
@@ -38,13 +38,7 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/third-party/dat.gui.min.js'></script>
-    <script src='../js/cottontail/build/cottontail.js'></script>
+    <script src='../js/third-party/dat.gui/dat.gui.min.js'></script>
 
     <script src='../js/webxr-button.js'></script>
   </head>
@@ -59,17 +53,18 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
+      import {CubeSeaNode} from '../js/cottontail/src/nodes/cube-sea.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // Helper methods for saving dat.gui-edited parameters in the URL hash.
       // Only non-default values are saved.
@@ -165,9 +160,14 @@
         // Viewport scale
         viewportScale: getFloatParam('viewportScale', 1),
 
-        // Prevents the app from clearing the background, which is useful for
-        // AR testing.
-        noClear: getBoolParam('noClear', false),
+        // Immersive AR mode
+        arMode: getBoolParam('arMode', false),
+
+        // Use an inline canvas for "magic window" mode?
+        magicWindow: getBoolParam('magicWindow', true),
+
+        // Use a mirror canvas during immersive mode?
+        mirrorCanvas: getBoolParam('mirrorCanvas', true),
 
         // Don't produce frames at all. Simulates a broken app.
         noFrames: getBoolParam('noFrames', false),
@@ -211,8 +211,12 @@
           getParamSaver('viewportScale'));
       folderRender.add(appSettings, 'antialias').onFinishChange(
           getParamSaver('antialias'));
-      folderRender.add(appSettings, 'noClear').onFinishChange(
-          getParamSaver('noClear'));
+      folderRender.add(appSettings, 'arMode').onFinishChange(
+          getParamSaver('arMode', () => { window.location.reload(false); }));
+      folderRender.add(appSettings, 'magicWindow').onFinishChange(
+          getParamSaver('magicWindow', () => { window.location.reload(false); }));
+      folderRender.add(appSettings, 'mirrorCanvas').onFinishChange(
+          getParamSaver('mirrorCanvas'));
       folderRender.open();
 
       var folderTest = gui.addFolder('Regression testing');
@@ -244,8 +248,8 @@
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -254,29 +258,34 @@
       scene.addNode(cubeSea);
 
       function initXR() {
-        xrButton = new XRDeviceButton({
+        let buttonOptions = {
           onRequestSession: onRequestSession,
           onEndSession: onEndSession
-        });
+        };
+        if (appSettings.arMode) {
+          buttonOptions.textEnterXRTitle = "START AR";
+          buttonOptions.textXRNotFoundTitle = "AR NOT FOUND";
+          buttonOptions.textExitXRTitle = "EXIT  AR";
+        }
+        xrButton = new XRDeviceButton(buttonOptions);
         document.querySelector('header').appendChild(xrButton.domElement);
 
+        let xrMode = appSettings.arMode ? 'immersive-ar' : 'immersive-vr';
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
+          navigator.xr.supportsSessionMode(xrMode).then(() => {
+            xrButton.enabled = true;
+          });
 
+          // Set up "magic window" mode.
+          if (appSettings.magicWindow) {
             let outputCanvas = document.createElement('canvas');
             let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({outputContext: ctx})
+            navigator.xr.requestSession({outputContext: ctx})
                 .then((session) => {
                   document.body.appendChild(outputCanvas);
                   onSessionStarted(session);
                 });
-          }).catch(() => {
-            initFallback();
-          });
+          }
         } else {
           initFallback();
         }
@@ -288,12 +297,12 @@
         let fallbackHelper = new FallbackHelper(scene, gl);
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         // Set up a non-black clear color so that we can see if something renders wrong.
@@ -301,17 +310,18 @@
 
         renderer = new Renderer(gl);
         scene.setRenderer(renderer);
-        scene.inputRenderer.setControllerMesh(new Gltf2Node({url: '../media/gltf/controller/controller.gltf'}));
+        scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'https://immersive-web.github.io/webxr-samples/media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
-        // Set up a mirror canvas
-        let mirrorCanvas = document.createElement('canvas');
-        let ctx = mirrorCanvas.getContext('xrpresent');
-        mirrorCanvas.setAttribute('id', 'mirror-canvas');
-        document.body.appendChild(mirrorCanvas);
-
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+      function onRequestSession() {
+        let xrOptions = { mode: appSettings.arMode ? 'immersive-ar' : 'immersive-vr' };
+        if (appSettings.mirrorCanvas) {
+          let mirrorCanvas = document.createElement('canvas');
+          xrOptions.outputContext = mirrorCanvas.getContext('xrpresent');
+          mirrorCanvas.setAttribute('id', 'mirror-canvas');
+          document.body.appendChild(mirrorCanvas);
+        }
+        navigator.xr.requestSession(xrOptions).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -320,17 +330,17 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl, {
           framebufferScaleFactor: appSettings.framebufferScale,
           antialias: appSettings.antialias});
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -341,10 +351,10 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive') && appSettings.mirrorCanvas) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
-          xrButton.setSession(null);
         }
+        xrButton.setSession(null);
       }
 
       let frameNum = 0;
@@ -352,10 +362,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         frameNum++;
 
@@ -388,9 +398,9 @@
           return;
         }
 
-        scene.clear = !appSettings.noClear;
+        scene.clear = !appSettings.arMode;
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -399,7 +409,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/offscreen-canvas.html b/third_party/webxr_test_pages/webxr-samples/tests/offscreen-canvas.html
index 9a714a16..0e4e369 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/offscreen-canvas.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/offscreen-canvas.html
@@ -38,13 +38,7 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/third-party/dat.gui.min.js'></script>
-    <script src='../js/cottontail/build/cottontail.js'></script>
+    <script src='../js/third-party/dat.gui/dat.gui.min.js'></script>
 
     <script src='../js/webxr-button.js'></script>
   </head>
@@ -59,22 +53,22 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+   <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from '../js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -91,30 +85,28 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({outputContext: ctx})
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({outputContext: ctx})
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         }
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         let offscreenCanvas = new OffscreenCanvas(16, 16);
         gl = offscreenCanvas.getContext('webgl', {
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -122,14 +114,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: '../media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -138,15 +130,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -157,7 +149,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -165,16 +157,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -183,7 +175,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html b/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
index f85a6d093..1a0828a2 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/permission-request.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/cottontail/build/cottontail.js'></script>
-
     <script src='../js/webxr-button.js'></script>
   </head>
   <body>
@@ -58,17 +51,20 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
+      import {UrlTexture} from '../js/cottontail/src/core/texture.js';
+      import {ButtonNode} from '../js/cottontail/src/nodes/button.js';
+      import {mat4} from '../js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       const BUTTON_PER_ROW = 5;
       const BUTTON_ROW_ARC = Math.PI * 0.2;
@@ -78,8 +74,8 @@
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -99,22 +95,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -127,12 +119,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -211,14 +203,14 @@
         ]);
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -228,21 +220,21 @@
         session.addEventListener('end', onSessionEnded);
 
         session.addEventListener('select', (ev) => {
-          let frameOfRef = ev.frame.session.immersive ?
-                           xrImmersiveFrameOfRef :
-                           xrNonImmersiveFrameOfRef;
-          scene.handleSelect(ev.inputSource, ev.frame, frameOfRef);
+          let refSpace = ev.frame.session.mode.startsWith('immersive') ?
+                           xrImmersiveRefSpace :
+                           xrNonImmersiveRefSpace;
+          scene.handleSelect(ev.inputSource, ev.frame, refSpace);
         });
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -254,7 +246,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -262,16 +254,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -280,7 +272,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/pointer-painter.html b/third_party/webxr_test_pages/webxr-samples/tests/pointer-painter.html
index 20e5f73e..792a8c30a 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/pointer-painter.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/pointer-painter.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/cottontail/build/cottontail.js'></script>
-
     <script src='../js/webxr-button.js'></script>
   </head>
   <body>
@@ -58,22 +51,22 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -91,22 +84,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -119,12 +108,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -132,14 +121,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: '../media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -152,15 +141,15 @@
         session.addEventListener('selectend', onSelectEnd);
         session.addEventListener('select', onSelect);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -179,10 +168,10 @@
 
       function onSelect(ev) {
         let session = ev.frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let inputPose = ev.frame.getInputPose(ev.inputSource, frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let inputPose = ev.frame.getInputPose(ev.inputSource, refSpace);
         if (!inputPose) {
           return;
         }
@@ -205,11 +194,11 @@
         }
       }
 
-      function updateInputSources(session, frame, frameOfRef) {
+      function updateInputSources(session, frame, refSpace) {
         let inputSources = session.getInputSources();
 
         for (let inputSource of inputSources) {
-          let inputPose = frame.getInputPose(inputSource, frameOfRef);
+          let inputPose = frame.getInputPose(inputSource, refSpace);
 
           if (!inputPose) {
             continue;
@@ -230,7 +219,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -238,16 +227,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        updateInputSources(session, frame, frameOfRef);
+        updateInputSources(session, frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -257,7 +246,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/tests/sponza.html b/third_party/webxr_test_pages/webxr-samples/tests/sponza.html
index 89f04ca..d2d41d6 100644
--- a/third_party/webxr_test_pages/webxr-samples/tests/sponza.html
+++ b/third_party/webxr_test_pages/webxr-samples/tests/sponza.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='../js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='../js/webxr-version-shim.js'></script>
-
-    <script src='../js/cottontail/build/cottontail.js'></script>
-
     <script src='../js/webxr-button.js'></script>
   </head>
   <body>
@@ -57,25 +50,27 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+   <script type="module">
+      import {Scene} from '../js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from '../js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from '../js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from '../js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from '../js/cottontail/src/nodes/skybox.js';
+      import {FallbackHelper} from '../js/cottontail/src/util/fallback-helper.js';
+      import {quat} from '../js/cottontail/src/math/gl-matrix.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // Temporary
       let hideStats = QueryArgs.getBool('hideStats', false);
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -99,22 +94,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({ outputContext: ctx })
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({ outputContext: ctx })
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -127,12 +118,12 @@
         fallbackHelper.emulateStage = true;
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         renderer = new Renderer(gl);
@@ -143,14 +134,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: '../media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -163,15 +154,15 @@
         // performed some sort of primary input action and respond to it.
         session.addEventListener('select', onSelect);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('stage').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
 
           session.requestAnimationFrame(onXRFrame);
@@ -187,7 +178,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -195,16 +186,16 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -213,7 +204,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/viewport-scaling.html b/third_party/webxr_test_pages/webxr-samples/viewport-scaling.html
index 351c5ac6..fc8b02b 100644
--- a/third_party/webxr_test_pages/webxr-samples/viewport-scaling.html
+++ b/third_party/webxr_test_pages/webxr-samples/viewport-scaling.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -62,22 +55,23 @@
         </p>
       </details>
     </header>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {FallbackHelper} from './js/cottontail/src/util/fallback-helper.js';
+      import {CubeSeaNode} from './js/cottontail/src/nodes/cube-sea.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrImmersiveFrameOfRef = null;
-      let xrNonImmersiveFrameOfRef = null;
+      let xrImmersiveRefSpace = null;
+      let xrNonImmersiveRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -117,22 +111,18 @@
         document.querySelector('header').appendChild(xrButton.domElement);
 
         if (navigator.xr) {
-          navigator.xr.requestDevice().then((device) => {
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
-
-            let outputCanvas = document.createElement('canvas');
-            let ctx = outputCanvas.getContext('xrpresent');
-
-            device.requestSession({outputContext: ctx})
-                .then((session) => {
-                  document.body.appendChild(outputCanvas);
-                  onSessionStarted(session);
-                });
-          }).catch(() => {
-            initFallback();
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
+
+          let outputCanvas = document.createElement('canvas');
+          let ctx = outputCanvas.getContext('xrpresent');
+
+          navigator.xr.requestSession({outputContext: ctx})
+              .then((session) => {
+                document.body.appendChild(outputCanvas);
+                onSessionStarted(session);
+              });
         } else {
           initFallback();
         }
@@ -144,12 +134,12 @@
         let fallbackHelper = new FallbackHelper(scene, gl);
       }
 
-      function initGL(compatibleDevice) {
+      function initGL() {
         if (gl)
           return;
 
         gl = createWebGLContext({
-          compatibleXRDevice: compatibleDevice
+          xrCompatible: true
         });
 
         gl.clearColor(0.1, 0.2, 0.3, 1.0);
@@ -160,14 +150,14 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
       }
 
-      function onRequestSession(device) {
+      function onRequestSession() {
         // Set up a mirror canvas
         let mirrorCanvas = document.createElement('canvas');
         let ctx = mirrorCanvas.getContext('xrpresent');
         mirrorCanvas.setAttribute('id', 'mirror-canvas');
         document.body.appendChild(mirrorCanvas);
 
-        device.requestSession({ immersive: true, outputContext: ctx }).then((session) => {
+        navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
           xrButton.setSession(session);
           onSessionStarted(session);
         });
@@ -176,15 +166,15 @@
       function onSessionStarted(session) {
         session.addEventListener('end', onSessionEnded);
 
-        initGL(session.device);
+        initGL();
 
         session.baseLayer = new XRWebGLLayer(session, gl);
 
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          if (session.immersive) {
-            xrImmersiveFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          if (session.mode.startsWith('immersive')) {
+            xrImmersiveRefSpace = refSpace;
           } else {
-            xrNonImmersiveFrameOfRef = frameOfRef;
+            xrNonImmersiveRefSpace = refSpace;
           }
           session.requestAnimationFrame(onXRFrame);
         });
@@ -195,7 +185,7 @@
       }
 
       function onSessionEnded(event) {
-        if (event.session.immersive) {
+        if (event.session.mode.startsWith('immersive')) {
           document.body.removeChild(document.querySelector('#mirror-canvas'));
           xrButton.setSession(null);
         }
@@ -203,10 +193,10 @@
 
       function onXRFrame(t, frame) {
         let session = frame.session;
-        let frameOfRef = session.immersive ?
-                         xrImmersiveFrameOfRef :
-                         xrNonImmersiveFrameOfRef;
-        let pose = frame.getDevicePose(frameOfRef);
+        let refSpace = session.mode.startsWith('immersive') ?
+                         xrImmersiveRefSpace :
+                         xrNonImmersiveRefSpace;
+        let pose = frame.getViewerPose(refSpace);
 
         scene.startFrame();
 
@@ -215,7 +205,7 @@
 
         session.requestAnimationFrame(onXRFrame);
 
-        scene.updateInputSources(frame, frameOfRef);
+        scene.updateInputSources(frame, refSpace);
 
         scene.drawXRFrame(frame, pose);
 
@@ -224,7 +214,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/xr-barebones.html b/third_party/webxr_test_pages/webxr-samples/xr-barebones.html
index a462ce8..f01aa2b 100644
--- a/third_party/webxr_test_pages/webxr-samples/xr-barebones.html
+++ b/third_party/webxr_test_pages/webxr-samples/xr-barebones.html
@@ -33,10 +33,6 @@
 
     <link href='css/common.css' rel='stylesheet'></link>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
   </head>
   <body>
     <header>
@@ -54,17 +50,11 @@
     <main style='text-align: center;'>
       <p>Click 'Enter XR' to see content</p> 
     </main>
-    <script>
-      (function () {
-      'use strict';
-
-      var versionShim = new WebXRVersionShim();
-
+    <script type="module">
       // XR globals.
       let xrButton = document.getElementById('xr-button');
-      let xrDevice = null;
       let xrSession = null;
-      let xrFrameOfRef = null;
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -75,17 +65,13 @@
       function initXR() {
         // Is WebXR available on this UA?
         if (navigator.xr) {
-          // Request an XRDevice connected to the system.
-          navigator.xr.requestDevice().then((device) => {
-            xrDevice = device;
-            // If the device allows creation of exclusive sessions set it as the
-            // target of the 'Enter XR' button.
-            device.supportsSession({immersive: true}).then(() => {
-              // Updates the button to start an XR session when clicked.
-              xrButton.addEventListener('click', onButtonClicked);
-              xrButton.innerHTML = 'Enter XR';
-              xrButton.disabled = false;
-            });
+          // If the device allows creation of exclusive sessions set it as the
+          // target of the 'Enter XR' button.
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            // Updates the button to start an XR session when clicked.
+            xrButton.addEventListener('click', onButtonClicked);
+            xrButton.innerHTML = 'Enter XR';
+            xrButton.disabled = false;
           });
         }
       }
@@ -94,7 +80,7 @@
       // session already we'll request one, and if we do we'll end it.
       function onButtonClicked() {
         if (!xrSession) {
-          xrDevice.requestSession({immersive: true}).then(onSessionStarted);
+          navigator.xr.requestSession({mode: 'immersive-vr'}).then(onSessionStarted);
         } else {
           xrSession.end();
         }
@@ -114,7 +100,7 @@
         // with the XRDisplay we're presenting to.
         let canvas = document.createElement('canvas');
         gl = canvas.getContext('webgl', {
-          compatibleXRDevice: session.device
+          xrCompatible: true
         });
 
         // Use the new WebGL context to create a XRWebGLLayer and set it as the
@@ -125,8 +111,8 @@
         // Get a frame of reference, which is required for querying poses. In
         // this case an 'eye-level' frame of reference means that all poses will
         // be relative to the location where the XRDevice was first detected.
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
 
           // Inform the session that we're ready to begin drawing.
           session.requestAnimationFrame(onXRFrame);
@@ -161,7 +147,7 @@
 
         // Get the XRDevice pose relative to the Frame of Reference we created
         // earlier.
-        let pose = frame.getDevicePose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         // Getting the pose may fail if, for example, tracking is lost. So we
         // have to check to make sure that we got a valid pose before attempting
@@ -187,7 +173,7 @@
           // and draw them into the corresponding viewport here, but we're
           // keeping this sample slim so we're not bothering to draw any
           // geometry.
-          /*for (let view of frame.views) {
+          /*for (let view of pose.views) {
             let viewport = session.baseLayer.getViewport(view);
             gl.viewport(viewport.x, viewport.y,
                         viewport.width, viewport.height);
@@ -199,8 +185,6 @@
 
       // Start the XR application.
       initXR();
-
-    })();
     </script>
   </body>
 </html>
diff --git a/third_party/webxr_test_pages/webxr-samples/xr-presentation.html b/third_party/webxr_test_pages/webxr-samples/xr-presentation.html
index c1edb002..6b2d3375 100644
--- a/third_party/webxr_test_pages/webxr-samples/xr-presentation.html
+++ b/third_party/webxr_test_pages/webxr-samples/xr-presentation.html
@@ -38,13 +38,6 @@
     <!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
     <script src='js/webxr-polyfill.js'></script>
 
-    <!--This script patches up around implementation differences in past browser versions
-        so that the samples can always be written against the most recent spec changes.
-        It won't be necessary after the API has been officially shipped for a bit.-->
-    <script src='js/webxr-version-shim.js'></script>
-
-    <script src='js/cottontail/build/cottontail.js'></script>
-
     <script src='js/webxr-button.js'></script>
   </head>
   <body>
@@ -62,21 +55,21 @@
     <main style='text-align: center;'>
       <p>Click 'Enter XR' to see content</p> 
     </main>
-    <script>
-      (function () {
-      'use strict';
+    <script type="module">
+      import {Scene} from './js/cottontail/src/scenes/scene.js';
+      import {Renderer, createWebGLContext} from './js/cottontail/src/core/renderer.js';
+      import {Gltf2Node} from './js/cottontail/src/nodes/gltf2.js';
+      import {QueryArgs} from './js/cottontail/src/util/query-args.js';
+      import {SkyboxNode} from './js/cottontail/src/nodes/skybox.js';
 
       // If requested, initialize the WebXR polyfill
       if (QueryArgs.getBool('allowPolyfill', false)) {
         var polyfill = new WebXRPolyfill();
       }
-      // Apply the version shim after the polyfill is instantiated, to ensure
-      // that the polyfill also gets patched if necessary.
-      var versionShim = new WebXRVersionShim();
 
       // XR globals.
       let xrButton = null;
-      let xrFrameOfRef = null;
+      let xrRefSpace = null;
 
       // WebGL scene globals.
       let gl = null;
@@ -98,21 +91,18 @@
 
         // Is WebXR available on this UA?
         if (navigator.xr) {
-          // Request an XRDevice connected to the system.
-          navigator.xr.requestDevice().then((device) => {
-            // If the device allows creation of exclusive sessions set it as the
-            // target of the 'Enter XR' button.
-            device.supportsSession({immersive: true}).then(() => {
-              xrButton.setDevice(device);
-            });
+          // If the device allows creation of exclusive sessions set it as the
+          // target of the 'Enter XR' button.
+          navigator.xr.supportsSessionMode('immersive-vr').then(() => {
+            xrButton.enabled = true;
           });
         }
       }
 
       // Called when the user selects a device to present to. In response we
       // will request an exclusive session from that device.
-      function onRequestSession(device) {
-        device.requestSession({immersive: true}).then(onSessionStarted);
+      function onRequestSession() {
+        navigator.xr.requestSession({mode: 'immersive-vr'}).then(onSessionStarted);
       }
 
       // Called when we've successfully acquired a XRSession. In response we
@@ -129,7 +119,7 @@
         // Create a WebGL context to render with, initialized to be compatible
         // with the XRDisplay we're presenting to.
         gl = createWebGLContext({
-          compatibleXRDevice: session.device
+          xrCompatible: true
         });
 
         // Create a renderer with that GL context (this is just for the samples
@@ -147,8 +137,8 @@
         // Get a frame of reference, which is required for querying poses. In
         // this case an 'eye-level' frame of reference means that all poses will
         // be relative to the location where the XRDevice was first detected.
-        session.requestFrameOfReference('eye-level').then((frameOfRef) => {
-          xrFrameOfRef = frameOfRef;
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => {
+          xrRefSpace = refSpace;
 
           // Inform the session that we're ready to begin drawing.
           session.requestAnimationFrame(onXRFrame);
@@ -185,13 +175,13 @@
 
         // Get the XRDevice pose relative to the Frame of Reference we created
         // earlier.
-        let pose = frame.getDevicePose(xrFrameOfRef);
+        let pose = frame.getViewerPose(xrRefSpace);
 
         // Getting the pose may fail if, for example, tracking is lost. So we
         // have to check to make sure that we got a valid pose before attempting
         // to render with it. If not in this case we'll just leave the
         // framebuffer cleared, so tracking loss means the scene will simply
-        // dissapear.
+        // disappear.
         if (pose) {
 
           // If we do have a valid pose, bind the WebGL layer's framebuffer,
@@ -204,7 +194,7 @@
 
           // Loop through each of the views reported by the frame and draw them
           // into the corresponding viewport.
-          for (let view of frame.views) {
+          for (let view of pose.views) {
             let viewport = session.baseLayer.getViewport(view);
             gl.viewport(viewport.x, viewport.y,
                         viewport.width, viewport.height);
@@ -216,7 +206,7 @@
             // projection and view matricies from the current view and pose.
             // We bound the framebuffer and viewport up above, and are passing
             // in the appropriate matrices here to be used when rendering.
-            scene.draw(view.projectionMatrix, pose.getViewMatrix(view));
+            scene.draw(view.projectionMatrix, view.viewMatrix);
           }
         } else {
           // There's several options for handling cases where no pose is given.
@@ -238,7 +228,6 @@
 
       // Start the XR application.
       initXR();
-      })();
     </script>
   </body>
 </html>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 083a206..feaf30b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -125074,6 +125074,17 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.HTMLDocumentParser.PreloadScannerAppCacheDelayTime"
+    units="microseconds" expires_after="2019-06-30">
+  <owner>yoavweiss@chromium.org</owner>
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    Time in which requests have been delayed after being discovered by the
+    HTMLPreloadScanner, due to document element not being yet available, which
+    is required for AppCache support. Recorded per document.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.IndexedDB.BackingStore.ConsistencyError"
     enum="IDBLevelDBBackingStoreInternalErrorType">
   <owner>dgrogan@chromium.org</owner>
diff --git a/ui/aura/mus/drag_drop_controller_mus.cc b/ui/aura/mus/drag_drop_controller_mus.cc
index 719abb1..43452de 100644
--- a/ui/aura/mus/drag_drop_controller_mus.cc
+++ b/ui/aura/mus/drag_drop_controller_mus.cc
@@ -141,10 +141,10 @@
   DCHECK(!current_drag_state_);
 
   base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
-  WindowMus* root_window_mus = WindowMus::Get(root_window);
+  WindowMus* source_window_mus = WindowMus::Get(source_window);
   const uint32_t change_id =
-      drag_drop_controller_host_->CreateChangeIdForDrag(root_window_mus);
-  CurrentDragState current_drag_state = {root_window_mus->server_id(),
+      drag_drop_controller_host_->CreateChangeIdForDrag(source_window_mus);
+  CurrentDragState current_drag_state = {source_window_mus->server_id(),
                                          change_id, ws::mojom::kDropEffectNone,
                                          data, run_loop.QuitClosure()};
 
@@ -166,7 +166,7 @@
     observer.OnDragStarted();
 
   window_tree_->PerformDragDrop(
-      change_id, root_window_mus->server_id(), screen_location,
+      change_id, source_window_mus->server_id(), screen_location,
       mojo::MapToFlatMap(drag_data), data.provider().GetDragImage(),
       data.provider().GetDragImageOffset(), drag_operations, mojo_source);
 
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index ae0407d..95db99f 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -57,6 +57,12 @@
   // Get the internal format of the image.
   virtual unsigned GetInternalFormat() = 0;
 
+  enum BindOrCopy { BIND, COPY };
+  // Returns whether this image is meant to be bound or copied to textures. The
+  // suggested method is not guaranteed to succeed, but the alternative will
+  // definitely fail.
+  virtual BindOrCopy ShouldBindOrCopy() = 0;
+
   // Bind image to texture currently bound to |target|. Returns true on success.
   // It is valid for an implementation to always return false.
   virtual bool BindTexImage(unsigned target) = 0;
diff --git a/ui/gl/gl_image_dxgi.cc b/ui/gl/gl_image_dxgi.cc
index 42ff203..3fdd1b01 100644
--- a/ui/gl/gl_image_dxgi.cc
+++ b/ui/gl/gl_image_dxgi.cc
@@ -148,6 +148,10 @@
   return static_cast<GLImageDXGI*>(image);
 }
 
+GLImageDXGI::BindOrCopy GLImageDXGI::ShouldBindOrCopy() {
+  return BIND;
+}
+
 bool GLImageDXGI::BindTexImage(unsigned target) {
   if (!handle_.Get())
     return true;
@@ -180,6 +184,7 @@
 }
 
 bool GLImageDXGI::CopyTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
diff --git a/ui/gl/gl_image_dxgi.h b/ui/gl/gl_image_dxgi.h
index d685d93..aab1031 100644
--- a/ui/gl/gl_image_dxgi.h
+++ b/ui/gl/gl_image_dxgi.h
@@ -27,6 +27,7 @@
   static GLImageDXGI* FromGLImage(GLImage* image);
 
   // GLImage implementation.
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
   bool CopyTexSubImage(unsigned target,
diff --git a/ui/gl/gl_image_egl.cc b/ui/gl/gl_image_egl.cc
index 9e1e66e..e229679 100644
--- a/ui/gl/gl_image_egl.cc
+++ b/ui/gl/gl_image_egl.cc
@@ -41,10 +41,13 @@
   return size_;
 }
 
+GLImageEGL::BindOrCopy GLImageEGL::ShouldBindOrCopy() {
+  return egl_image_ == EGL_NO_IMAGE_KHR ? COPY : BIND;
+}
+
 bool GLImageEGL::BindTexImage(unsigned target) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (egl_image_ == EGL_NO_IMAGE_KHR)
-    return false;
+  DCHECK_EQ(BIND, ShouldBindOrCopy());
 
   glEGLImageTargetTexture2DOES(target, egl_image_);
   DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
diff --git a/ui/gl/gl_image_egl.h b/ui/gl/gl_image_egl.h
index e014dc77..2d0a5725 100644
--- a/ui/gl/gl_image_egl.h
+++ b/ui/gl/gl_image_egl.h
@@ -21,6 +21,7 @@
 
   // Overridden from GLImage:
   gfx::Size GetSize() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override {}
 
diff --git a/ui/gl/gl_image_glx.cc b/ui/gl/gl_image_glx.cc
index f54a062..ecaf87f6 100644
--- a/ui/gl/gl_image_glx.cc
+++ b/ui/gl/gl_image_glx.cc
@@ -146,6 +146,10 @@
 
 unsigned GLImageGLX::GetInternalFormat() { return internalformat_; }
 
+GLImageGLX::BindOrCopy GLImageGLX::ShouldBindOrCopy() {
+  return BIND;
+}
+
 bool GLImageGLX::BindTexImage(unsigned target) {
   if (!glx_pixmap_)
     return false;
@@ -166,6 +170,7 @@
 }
 
 bool GLImageGLX::CopyTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
diff --git a/ui/gl/gl_image_glx.h b/ui/gl/gl_image_glx.h
index dd5ee13..646753e 100644
--- a/ui/gl/gl_image_glx.h
+++ b/ui/gl/gl_image_glx.h
@@ -24,6 +24,7 @@
   // Overridden from GLImage:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override;
   bool CopyTexImage(unsigned target) override;
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h
index dc15f725..2592478 100644
--- a/ui/gl/gl_image_io_surface.h
+++ b/ui/gl/gl_image_io_surface.h
@@ -46,6 +46,7 @@
   // Overridden from GLImage:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   bool BindTexImageWithInternalformat(unsigned target,
                                       unsigned internalformat) override;
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm
index ae340a0a..751fa4cf 100644
--- a/ui/gl/gl_image_io_surface.mm
+++ b/ui/gl/gl_image_io_surface.mm
@@ -244,6 +244,13 @@
   return internalformat_;
 }
 
+GLImageIOSurface::BindOrCopy GLImageIOSurface::ShouldBindOrCopy() {
+  // YUV_420_BIPLANAR is not supported by BindTexImage.
+  // CopyTexImage is supported by this format as that performs conversion to RGB
+  // as part of the copy operation.
+  return format_ == gfx::BufferFormat::YUV_420_BIPLANAR ? COPY : BIND;
+}
+
 bool GLImageIOSurface::BindTexImage(unsigned target) {
   return BindTexImageWithInternalformat(target, 0);
 }
@@ -251,15 +258,10 @@
 bool GLImageIOSurface::BindTexImageWithInternalformat(unsigned target,
                                                       unsigned internalformat) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(BIND, ShouldBindOrCopy());
   TRACE_EVENT0("gpu", "GLImageIOSurface::BindTexImage");
   base::TimeTicks start_time = base::TimeTicks::Now();
 
-  // YUV_420_BIPLANAR is not supported by BindTexImage.
-  // CopyTexImage is supported by this format as that performs conversion to RGB
-  // as part of the copy operation.
-  if (format_ == gfx::BufferFormat::YUV_420_BIPLANAR)
-    return false;
-
   if (target != GL_TEXTURE_RECTANGLE_ARB) {
     // This might be supported in the future. For now, perform strict
     // validation so we know what's going on.
@@ -299,9 +301,7 @@
 
 bool GLImageIOSurface::CopyTexImage(unsigned target) {
   DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (format_ != gfx::BufferFormat::YUV_420_BIPLANAR)
-    return false;
+  DCHECK_EQ(COPY, ShouldBindOrCopy());
 
   GLContext* gl_context = GLContext::GetCurrent();
   DCHECK(gl_context);
diff --git a/ui/gl/gl_image_memory.cc b/ui/gl/gl_image_memory.cc
index 4f0641c..14b6fcf 100644
--- a/ui/gl/gl_image_memory.cc
+++ b/ui/gl/gl_image_memory.cc
@@ -316,7 +316,12 @@
   return TextureFormat(format_);
 }
 
+GLImage::BindOrCopy GLImageMemory::ShouldBindOrCopy() {
+  return COPY;
+}
+
 bool GLImageMemory::BindTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
@@ -324,7 +329,6 @@
   TRACE_EVENT2("gpu", "GLImageMemory::CopyTexImage", "width", size_.width(),
                "height", size_.height());
 
-  // GL_TEXTURE_EXTERNAL_OES is not a supported target.
   if (target == GL_TEXTURE_EXTERNAL_OES)
     return false;
 
diff --git a/ui/gl/gl_image_memory.h b/ui/gl/gl_image_memory.h
index 3e9aea8..9ba081b1 100644
--- a/ui/gl/gl_image_memory.h
+++ b/ui/gl/gl_image_memory.h
@@ -30,6 +30,7 @@
   // Overridden from GLImage:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override {}
   bool CopyTexImage(unsigned target) override;
diff --git a/ui/gl/gl_image_stub.cc b/ui/gl/gl_image_stub.cc
index c1d3363f..9fe966f6 100644
--- a/ui/gl/gl_image_stub.cc
+++ b/ui/gl/gl_image_stub.cc
@@ -20,9 +20,14 @@
 
 unsigned GLImageStub::GetInternalFormat() { return GL_RGBA; }
 
+GLImageStub::BindOrCopy GLImageStub::ShouldBindOrCopy() {
+  return BIND;
+}
+
 bool GLImageStub::BindTexImage(unsigned target) { return true; }
 
 bool GLImageStub::CopyTexImage(unsigned target) {
+  NOTREACHED();
   return false;
 }
 
diff --git a/ui/gl/gl_image_stub.h b/ui/gl/gl_image_stub.h
index 745a6c43..60fb0ed1 100644
--- a/ui/gl/gl_image_stub.h
+++ b/ui/gl/gl_image_stub.h
@@ -20,6 +20,7 @@
   // Overridden from GLImage:
   gfx::Size GetSize() override;
   unsigned GetInternalFormat() override;
+  BindOrCopy ShouldBindOrCopy() override;
   bool BindTexImage(unsigned target) override;
   void ReleaseTexImage(unsigned target) override {}
   bool CopyTexImage(unsigned target) override;
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 4a1b7da..1feb3e9f 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -99,6 +99,8 @@
         return gfx::kGoogleRed300;
       case NativeTheme::kColorId_ThrobberSpinningColor:
         return gfx::kGoogleBlue300;
+      case NativeTheme::kColorId_DefaultIconColor:
+        return gfx::kGoogleGrey500;
       default:
         break;
     }
@@ -319,6 +321,9 @@
     case NativeTheme::kColorId_AlertSeverityHigh:
       return gfx::kGoogleRed600;
 
+    case NativeTheme::kColorId_DefaultIconColor:
+      return gfx::kGoogleGrey700;
+
     case NativeTheme::kColorId_NumColors:
       break;
   }
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 796fe26..34850d70 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -381,6 +381,8 @@
     kColorId_AlertSeverityLow,
     kColorId_AlertSeverityMedium,
     kColorId_AlertSeverityHigh,
+    // Colors for icons in secondary UI (content settings, help button, etc).
+    kColorId_DefaultIconColor,
     // TODO(benrg): move other hardcoded colors here.
 
     kColorId_NumColors,
diff --git a/ui/native_theme/native_theme_dark_aura.cc b/ui/native_theme/native_theme_dark_aura.cc
index 54bbee09..3f143c6 100644
--- a/ui/native_theme/native_theme_dark_aura.cc
+++ b/ui/native_theme/native_theme_dark_aura.cc
@@ -80,6 +80,7 @@
     case kColorId_ResultsTableHoveredBackground:
     case kColorId_ProminentButtonDisabledColor:
     case kColorId_ButtonBorderColor:
+    case kColorId_DefaultIconColor:
       return NativeThemeAura::GetSystemColor(color_id);
 
     // Any other color is not defined and shouldn't be used in a dark theme.
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
index e657ac8a..21e13af 100644
--- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
+++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
@@ -50,8 +50,8 @@
   return true;
 }
 
-VkInstance VulkanImplementationGbm::GetVulkanInstance() {
-  return vulkan_instance_.vk_instance();
+gpu::VulkanInstance* VulkanImplementationGbm::GetVulkanInstance() {
+  return &vulkan_instance_;
 }
 
 std::unique_ptr<gpu::VulkanSurface> VulkanImplementationGbm::CreateViewSurface(
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
index c619196..182f43e 100644
--- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
+++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
@@ -19,7 +19,7 @@
 
   // VulkanImplementation:
   bool InitializeVulkanInstance() override;
-  VkInstance GetVulkanInstance() override;
+  gpu::VulkanInstance* GetVulkanInstance() override;
   std::unique_ptr<gpu::VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) override;
   bool GetPhysicalDevicePresentationSupport(
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
index d2a4379d..72184009 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -64,8 +64,8 @@
   return true;
 }
 
-VkInstance VulkanImplementationScenic::GetVulkanInstance() {
-  return vulkan_instance_.vk_instance();
+gpu::VulkanInstance* VulkanImplementationScenic::GetVulkanInstance() {
+  return &vulkan_instance_;
 }
 
 std::unique_ptr<gpu::VulkanSurface>
@@ -83,14 +83,15 @@
       image_pipe.Unbind().TakeChannel().release();
 
   VkResult result = vkCreateImagePipeSurfaceFUCHSIA_(
-      GetVulkanInstance(), &surface_create_info, nullptr, &surface);
+      vulkan_instance_.vk_instance(), &surface_create_info, nullptr, &surface);
   if (result != VK_SUCCESS) {
     // This shouldn't fail, and we don't know whether imagePipeHandle was closed
     // if it does.
     LOG(FATAL) << "vkCreateImagePipeSurfaceFUCHSIA failed: " << result;
   }
 
-  return std::make_unique<gpu::VulkanSurface>(GetVulkanInstance(), surface);
+  return std::make_unique<gpu::VulkanSurface>(vulkan_instance_.vk_instance(),
+                                              surface);
 }
 
 bool VulkanImplementationScenic::GetPhysicalDevicePresentationSupport(
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
index e61afca..7653993 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
@@ -23,7 +23,7 @@
 
   // VulkanImplementation:
   bool InitializeVulkanInstance() override;
-  VkInstance GetVulkanInstance() override;
+  gpu::VulkanInstance* GetVulkanInstance() override;
   std::unique_ptr<gpu::VulkanSurface> CreateViewSurface(
       gfx::AcceleratedWidget window) override;
   bool GetPhysicalDevicePresentationSupport(