diff --git a/DEPS b/DEPS index b87e2f6..3748fc47 100644 --- a/DEPS +++ b/DEPS
@@ -144,11 +144,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '7fbb36316248b69cc9e04710d248ca978fa1a79c', + 'skia_revision': 'be91f062d70e2f317298632f172bde85255af564', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'b39aa31e76b6f7be51bdc252481e0e89a48c97ab', + 'v8_revision': '098b541adc9bb3b953688881354e7a4f948576cd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -156,11 +156,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '901dd5458e48d27433878483de291b5e72db1281', + 'angle_revision': '9ec3f51d11d92c8b87da27811cc7ab577705ea3c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'ab67c12d321108fc88e42775632f898916fa96de', + 'swiftshader_revision': '3af883cd54dfbe85e1c7b7b64287fd294dd38e25', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -207,7 +207,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': '4f6d452ceedc1c7dcdd0743f243dcc6a0b082bf4', + 'catapult_revision': '5075be939f844c14e43d6a8a16b67d6c6df08520', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -223,7 +223,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. - 'feed_revision': '6bd5f95e33bfa4ce1be7948c28d773e7d9247825', + 'feed_revision': '24f0eac37fc3d70ff410b0391f7a0cadc018df9a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_build-tools_version # and whatever else without interference from each other. @@ -808,7 +808,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9cc9feed6cce229e36b5216ce63634a3d4f5425d', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '337fee4fd28a163a9062334847e4c2303bf3aed0', 'condition': 'checkout_linux', }, @@ -833,7 +833,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f4e0cc69eed38336e8e6e741c7b6ad02e9cab731', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '5e67857cb3158ba6dd24ff3ddcd39b3018a78bb0', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1178,7 +1178,7 @@ Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf', 'src/third_party/openscreen/src': - Var('chromium_git') + '/openscreen' + '@' + 'e0c3eedc10f2e43abd1a6c118b58d0613280fc24', + Var('chromium_git') + '/openscreen' + '@' + 'eb5dbc61ce239d7b7bba9d9400553906acadcae4', 'src/third_party/openxr/src': { 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'c52d3b40dd802353c1f2ceec139922b374c1373a', @@ -1206,7 +1206,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '801bb265be0922d0a744650b93f022ee278f821a', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a4960e4da728676aa7441308ca479086e4d252e4', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1276,7 +1276,7 @@ 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': '-ZVn1C00lIdjTSJqwdg9GyT_jqzY8RmSrZ7NGfQFeZ4C', + 'version': '1ceH-5pheRe3OUeFz4TzVc2xcu6sm45yeFQBTT4rd6IC', }, ], 'condition': 'checkout_android', @@ -1415,7 +1415,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@90bf26326f4bc1bddf5253d9a0fadb4962a412ee', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@186e2346ab7ee5d38f197540f1d3d72d3a2a7eac', 'condition': 'checkout_src_internal', },
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc index 396bd88..bf7182f4 100644 --- a/ash/autoclick/autoclick_unittest.cc +++ b/ash/autoclick/autoclick_unittest.cc
@@ -20,7 +20,6 @@ #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" -#include "ui/accessibility/accessibility_switches.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" @@ -126,16 +125,6 @@ AshTestBase::TearDown(); } - void EnableExperimentalAutoclickFlag(bool enable) { - if (enable) { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableExperimentalAccessibilityAutoclick); - } else { - base::CommandLine::ForCurrentProcess()->RemoveSwitch( - switches::kEnableExperimentalAccessibilityAutoclick); - } - } - void MoveMouseWithFlagsTo(int x, int y, ui::EventFlags flags) { GetEventGenerator()->set_flags(flags); GetEventGenerator()->MoveMouseTo(x, y); @@ -629,7 +618,6 @@ TEST_F(AutoclickTest, AutoclickScrollEvents) { UpdateDisplay("800x600"); - EnableExperimentalAutoclickFlag(true); GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->SetAutoclickEventType(AutoclickEventType::kScroll); std::vector<ui::MouseEvent> events; @@ -697,8 +685,6 @@ EXPECT_EQ(gfx::Point(200, 200), wheel_events[0].location()); EXPECT_LT(wheel_events[0].x_offset(), 0); ClearMouseEvents(); - - EnableExperimentalAutoclickFlag(false); } TEST_F(AutoclickTest, AutoclickRevertsToLeftClick) { @@ -1215,7 +1201,6 @@ } TEST_F(AutoclickTest, ScrollClosesWhenHoveredOverScrollButton) { - EnableExperimentalAutoclickFlag(true); GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->SetAutoclickEventType( AutoclickEventType::kLeftClick); @@ -1238,13 +1223,10 @@ EXPECT_EQ(AutoclickEventType::kLeftClick, Shell::Get()->accessibility_controller()->GetAutoclickEventType()); EXPECT_FALSE(GetAutoclickScrollView()); - - EnableExperimentalAutoclickFlag(false); } TEST_F(AutoclickTest, ScrollOccursWhenHoveredOverScrollButtons) { UpdateDisplay("800x600"); - EnableExperimentalAutoclickFlag(true); GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); // Enable scroll. @@ -1304,13 +1286,10 @@ EXPECT_EQ(0u, events.size()); ClearMouseEvents(); } - - EnableExperimentalAutoclickFlag(false); } TEST_F(AutoclickTest, ScrollMenuBubblePostioning) { UpdateDisplay("800x600"); - EnableExperimentalAutoclickFlag(true); GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( @@ -1367,8 +1346,6 @@ Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( AutoclickMenuPosition::kBottomRight); EXPECT_EQ(GetAutoclickScrollView()->GetBoundsInScreen(), scroll_bounds); - - EnableExperimentalAutoclickFlag(false); } } // namespace ash
diff --git a/ash/shelf/overflow_bubble_view.cc b/ash/shelf/overflow_bubble_view.cc index 10e343e..beaa5a1 100644 --- a/ash/shelf/overflow_bubble_view.cc +++ b/ash/shelf/overflow_bubble_view.cc
@@ -155,7 +155,7 @@ void Initialize(); // Translate |shelf_view_| by |offset|. - void TranslateShelfView(const gfx::Vector2d& offset); + void TranslateShelfView(const gfx::Vector2dF& offset); // views::View: gfx::Size CalculatePreferredSize() const override; @@ -205,7 +205,7 @@ } void OverflowBubbleView::OverflowShelfContainerView::TranslateShelfView( - const gfx::Vector2d& offset) { + const gfx::Vector2dF& offset) { gfx::Transform transform_matrix; transform_matrix.Translate(-offset); shelf_view_->SetTransform(transform_matrix); @@ -288,33 +288,30 @@ if (event.type() != ui::ET_GESTURE_SCROLL_UPDATE) return false; - if (shelf_->IsHorizontalAlignment()) { - ScrollByXOffset(static_cast<int>(-event.details().scroll_x()), - /*animate=*/false); - } else { - ScrollByYOffset(static_cast<int>(-event.details().scroll_y()), - /*animate=*/false); - } + if (shelf_->IsHorizontalAlignment()) + ScrollByXOffset(-event.details().scroll_x(), /*animate=*/false); + else + ScrollByYOffset(-event.details().scroll_y(), /*animate=*/false); return true; } -int OverflowBubbleView::ScrollByXOffset(int x_offset, bool animating) { - const int old_x = scroll_offset_.x(); - const int x = CalculateLayoutStrategyAfterScroll(x_offset); +int OverflowBubbleView::ScrollByXOffset(float x_offset, bool animating) { + const float old_x = scroll_offset_.x(); + const float x = CalculateLayoutStrategyAfterScroll(x_offset); scroll_offset_.set_x(x); Layout(); - const int diff = x - old_x; + const float diff = x - old_x; if (animating) StartShelfScrollAnimation(diff); return diff; } -int OverflowBubbleView::ScrollByYOffset(int y_offset, bool animating) { +int OverflowBubbleView::ScrollByYOffset(float y_offset, bool animating) { const int old_y = scroll_offset_.y(); const int y = CalculateLayoutStrategyAfterScroll(y_offset); scroll_offset_.set_y(y); Layout(); - const int diff = y - old_y; + const float diff = y - old_y; if (animating) StartShelfScrollAnimation(diff); return diff; @@ -339,14 +336,15 @@ return preferred_length - available_length; } -int OverflowBubbleView::CalculateLayoutStrategyAfterScroll(int scroll) { - const int old_scroll = +float OverflowBubbleView::CalculateLayoutStrategyAfterScroll(float scroll) { + const float old_scroll = shelf_->IsHorizontalAlignment() ? scroll_offset_.x() : scroll_offset_.y(); - const int scroll_upper_bound = CalculateScrollUpperBound(); - scroll = std::min(scroll_upper_bound, std::max(0, old_scroll + scroll)); + const float scroll_upper_bound = + static_cast<float>(CalculateScrollUpperBound()); + scroll = std::min(scroll_upper_bound, std::max(0.f, old_scroll + scroll)); if (layout_strategy_ != NOT_SHOW_ARROW_BUTTON) { - if (scroll <= 0) + if (scroll <= 0.f) layout_strategy_ = SHOW_RIGHT_ARROW_BUTTON; else if (scroll >= scroll_upper_bound) layout_strategy_ = SHOW_LEFT_ARROW_BUTTON; @@ -376,13 +374,13 @@ } } -void OverflowBubbleView::StartShelfScrollAnimation(int scroll_distance) { +void OverflowBubbleView::StartShelfScrollAnimation(float scroll_distance) { const gfx::Transform current_transform = shelf_view()->GetTransform(); gfx::Transform reverse_transform = current_transform; if (shelf_->IsHorizontalAlignment()) - reverse_transform.Translate(gfx::Vector2d(scroll_distance, 0)); + reverse_transform.Translate(gfx::Vector2dF(scroll_distance, 0)); else - reverse_transform.Translate(gfx::Vector2d(0, scroll_distance)); + reverse_transform.Translate(gfx::Vector2dF(0, scroll_distance)); shelf_view()->layer()->SetTransform(reverse_transform); ui::ScopedLayerAnimationSettings animation_settings( shelf_view()->layer()->GetAnimator());
diff --git a/ash/shelf/overflow_bubble_view.h b/ash/shelf/overflow_bubble_view.h index aacc6c7..16280fc 100644 --- a/ash/shelf/overflow_bubble_view.h +++ b/ash/shelf/overflow_bubble_view.h
@@ -47,9 +47,10 @@ bool ProcessGestureEvent(const ui::GestureEvent& event); // These return the actual offset (sometimes reduced by the clamping). - // |animating| indicates whether animation is needed for scrolling. - int ScrollByXOffset(int x_offset, bool animating); - int ScrollByYOffset(int y_offset, bool animating); + // |animating| indicates whether animation is needed for scrolling. |x_offset| + // or |y_offset| has to be float. Otherwise the slow gesture drag is neglected + int ScrollByXOffset(float x_offset, bool animating); + int ScrollByYOffset(float y_offset, bool animating); // views::BubbleDialogDelegateView: gfx::Rect GetBubbleBounds() override; @@ -59,7 +60,7 @@ View* left_arrow() { return left_arrow_; } View* right_arrow() { return right_arrow_; } LayoutStrategy layout_strategy() const { return layout_strategy_; } - gfx::Vector2d scroll_offset() const { return scroll_offset_; } + gfx::Vector2dF scroll_offset() const { return scroll_offset_; } // ShelfBubble: bool ShouldCloseOnPressDown() override; @@ -85,7 +86,7 @@ // Updates the overflow bubble view's layout strategy after scrolling by the // distance of |scroll|. Returns the adapted scroll offset. - int CalculateLayoutStrategyAfterScroll(int scroll); + float CalculateLayoutStrategyAfterScroll(float scroll); // Ensures that the width of |bubble_bounds| (if it is not horizontally // aligned, adjust |bubble_bounds|'s height) is the multiple of the sum of @@ -94,7 +95,7 @@ void AdjustToEnsureIconsFullyVisible(gfx::Rect* bubble_bounds) const; // Creates the animation for scrolling shelf by |scroll_distance|. - void StartShelfScrollAnimation(int scroll_distance); + void StartShelfScrollAnimation(float scroll_distance); // views::View: gfx::Size CalculatePreferredSize() const override; @@ -120,7 +121,7 @@ Shelf* shelf_; ShelfView* shelf_view_; - gfx::Vector2d scroll_offset_; + gfx::Vector2dF scroll_offset_; DISALLOW_COPY_AND_ASSIGN(OverflowBubbleView); };
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc index f4c237b1..835abe9 100644 --- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc +++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -16,8 +16,6 @@ #include "ash/wm/work_area_insets.h" #include "ash/wm/workspace/workspace_layout_manager.h" #include "ash/wm/workspace_controller.h" -#include "base/command_line.h" -#include "ui/accessibility/accessibility_switches.h" #include "ui/aura/window_tree_host.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" @@ -28,8 +26,7 @@ namespace { // Autoclick menu constants. -const int kAutoclickMenuWidth = 319; -const int kAutoclickMenuWidthWithScroll = 369; +const int kAutoclickMenuWidth = 369; const int kAutoclickMenuHeight = 64; AutoclickMenuPosition DefaultSystemPosition() { @@ -128,21 +125,17 @@ aura::Window* window = Shell::GetPrimaryRootWindow(); gfx::Rect work_area = WorkAreaInsets::ForWindow(window)->user_work_area_bounds(); - int width = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalAccessibilityAutoclick) - ? kAutoclickMenuWidthWithScroll - : kAutoclickMenuWidth; gfx::Rect new_bounds; switch (new_position) { case AutoclickMenuPosition::kBottomRight: - new_bounds = gfx::Rect(work_area.right() - width, - work_area.bottom() - kAutoclickMenuHeight, width, - kAutoclickMenuHeight); + new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth, + work_area.bottom() - kAutoclickMenuHeight, + kAutoclickMenuWidth, kAutoclickMenuHeight); break; case AutoclickMenuPosition::kBottomLeft: new_bounds = gfx::Rect(work_area.x(), work_area.bottom() - kAutoclickMenuHeight, - width, kAutoclickMenuHeight); + kAutoclickMenuWidth, kAutoclickMenuHeight); break; case AutoclickMenuPosition::kTopLeft: // Because there is no inset at the top of the widget, add @@ -150,16 +143,16 @@ // to ensure correct padding. new_bounds = gfx::Rect( work_area.x(), work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp, - width, kAutoclickMenuHeight); + kAutoclickMenuWidth, kAutoclickMenuHeight); break; case AutoclickMenuPosition::kTopRight: // Because there is no inset at the top of the widget, add // 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area. // to ensure correct padding. new_bounds = - gfx::Rect(work_area.right() - width, - work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp, width, - kAutoclickMenuHeight); + gfx::Rect(work_area.right() - kAutoclickMenuWidth, + work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp, + kAutoclickMenuWidth, kAutoclickMenuHeight); break; case AutoclickMenuPosition::kSystemDefault: return; @@ -225,12 +218,8 @@ init_params.insets = gfx::Insets(0, kCollisionWindowWorkAreaInsetsDp, kCollisionWindowWorkAreaInsetsDp, kCollisionWindowWorkAreaInsetsDp); - int width = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalAccessibilityAutoclick) - ? kAutoclickMenuWidthWithScroll - : kAutoclickMenuWidth; - init_params.min_width = width; - init_params.max_width = width; + init_params.min_width = kAutoclickMenuWidth; + init_params.max_width = kAutoclickMenuWidth; init_params.corner_radius = kUnifiedTrayCornerRadius; init_params.has_shadow = false; bubble_view_ = new AutoclickMenuBubbleView(init_params);
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc index b407f289..aba431d 100644 --- a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc +++ b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc
@@ -11,7 +11,6 @@ #include "ash/system/accessibility/autoclick_scroll_bubble_controller.h" #include "ash/test/ash_test_base.h" #include "base/command_line.h" -#include "ui/accessibility/accessibility_switches.h" namespace ash { @@ -44,8 +43,6 @@ // testing::Test: void SetUp() override { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableExperimentalAccessibilityAutoclick); AshTestBase::SetUp(); Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true); }
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc index 998d914..5af60e4 100644 --- a/ash/system/accessibility/autoclick_menu_view.cc +++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -10,10 +10,8 @@ #include "ash/strings/grit/ash_strings.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/unified/top_shortcut_button.h" -#include "base/command_line.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" -#include "ui/accessibility/accessibility_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" @@ -173,6 +171,9 @@ new AutoclickMenuButton(this, kAutoclickDragIcon, IDS_ASH_AUTOCLICK_OPTION_DRAG_AND_DROP)), + scroll_button_(new AutoclickMenuButton(this, + kAutoclickScrollIcon, + IDS_ASH_AUTOCLICK_OPTION_SCROLL)), pause_button_( new AutoclickMenuButton(this, kAutoclickPauseIcon, @@ -183,13 +184,6 @@ IDS_ASH_AUTOCLICK_OPTION_CHANGE_POSITION, kPanelPositionButtonSize, false /* no highlight */)) { - // TODO(katie): Initialize scroll above once it launches, target in M77. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalAccessibilityAutoclick)) { - scroll_button_ = new AutoclickMenuButton(this, kAutoclickScrollIcon, - IDS_ASH_AUTOCLICK_OPTION_SCROLL); - } - // Set view IDs for testing. left_click_button_->SetId(ButtonId::kLeftClick); right_click_button_->SetId(ButtonId::kRightClick);
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc index bf07c5ab..2240c84 100644 --- a/ash/system/network/network_list_view.cc +++ b/ash/system/network/network_list_view.cc
@@ -503,10 +503,9 @@ *separator_view = nullptr; } - bool default_toggle_enabled = !IsSecondaryUser(); // Mobile updates its toggle state independently. if (!NetworkTypeMatchesType(type, NetworkType::kMobile)) - view->SetToggleState(default_toggle_enabled, enabled /* is_on */); + view->SetToggleState(true /* toggle_enabled */, enabled /* is_on */); PlaceViewAtIndex(view, child_index++); return child_index; }
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc index 57c2adb5..77389c8 100644 --- a/ash/wm/desks/desk_preview_view.cc +++ b/ash/wm/desks/desk_preview_view.cc
@@ -91,7 +91,7 @@ // but we want to show a preview of the windows in their untransformed state // outside of overview mode. // TODO(afakhry): Is it safe to do this for all layers in the subtree, or - // should we limit this for the mirrors of the top level windows' layers. + // should we limit this for the mirrors of the top level windows' layers? mirror->SetTransform(gfx::Transform()); }
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc index 1b2e78c..9ffb1d5 100644 --- a/ash/wm/desks/desks_controller.cc +++ b/ash/wm/desks/desks_controller.cc
@@ -14,6 +14,8 @@ #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_item.h" +#include "ash/wm/splitview/split_view_controller.h" +#include "ash/wm/splitview/split_view_utils.h" #include "ash/wm/window_util.h" #include "base/auto_reset.h" #include "base/logging.h" @@ -148,8 +150,8 @@ DCHECK(!desks_.empty()); - const bool in_overview = - Shell::Get()->overview_controller()->InOverviewSession(); + auto* overview_controller = Shell::Get()->overview_controller(); + const bool in_overview = overview_controller->InOverviewSession(); const std::vector<aura::Window*> removed_desk_windows = removed_desk->windows(); @@ -160,7 +162,8 @@ // - Move windows in removed desk (if any) to the currently active desk. // - If the active desk is the one being removed, activate the desk to its // left, if no desk to the left, activate one on the right. - if (removed_desk.get() != active_desk_) { + const bool will_switch_desks = (removed_desk.get() == active_desk_); + if (!will_switch_desks) { // We will refresh the mini_views of the active desk only once at the end. auto active_desk_mini_view_pauser = active_desk_->GetScopedNotifyContentChangedDisabler(); @@ -190,6 +193,13 @@ auto target_desk_mini_view_pauser = target_desk->GetScopedNotifyContentChangedDisabler(); + // Exit split view if active, before activating the new desk. We will + // restore the split view state of the newly activated desk at the end. + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->EndSplitView( + SplitViewController::EndReason::kDesksChange); + // The removed desk is the active desk, so temporarily remove its windows // from the overview grid which will result in removing the // "OverviewModeLabel" widgets created by overview mode for these windows. @@ -205,6 +215,9 @@ ActivateDeskInternal(target_desk, /*update_window_activation=*/!in_overview); + // Desk activation should not change overview mode state. + DCHECK_EQ(in_overview, overview_controller->InOverviewSession()); + // Now that the windows from the removed and target desks merged, add them // all without animation to the grid in the order of their MRU. if (in_overview) @@ -229,6 +242,11 @@ if (in_overview) removed_desk->UpdateDeskBackdrops(); + // Restoring split view may start or end overview mode, therefore do this at + // the end to avoid getting into a bad state. + if (will_switch_desks) + MaybeRestoreSplitView(/*refresh_snapped_windows=*/true); + DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks); } @@ -321,15 +339,15 @@ for (auto* root : roots) root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(true); + // The order here matters. Overview must end before ending split view before + // switching desks. That's because we don't want TabletModeWindowManager + // maximizing all windows because we cleared the snapped ones in + // split_view_controller first. See: + // `TabletModeWindowManager::OnOverviewModeEndingAnimationComplete()`. + // See also test coverage for this case in: + // `TabletModeDesksTest.SnappedStateRetainedOnSwitchingDesksFromOverview`. const bool in_overview = Shell::Get()->overview_controller()->InOverviewSession(); - - ActivateDeskInternal(ending_desk, /*update_window_activation=*/true); - - // Activating a desk should not change the overview mode state. - DCHECK_EQ(in_overview, - Shell::Get()->overview_controller()->InOverviewSession()); - if (in_overview) { // Exit overview mode immediately without any animations before taking the // ending desk screenshot. This makes sure that the ending desk @@ -337,6 +355,14 @@ Shell::Get()->overview_controller()->EndOverview( OverviewSession::EnterExitOverviewType::kImmediateExit); } + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->EndSplitView( + SplitViewController::EndReason::kDesksChange); + + ActivateDeskInternal(ending_desk, /*update_window_activation=*/true); + + MaybeRestoreSplitView(/*refresh_snapped_windows=*/true); for (auto* root : roots) root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(false);
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc index 0c80ae6..c88b108 100644 --- a/ash/wm/desks/desks_unittests.cc +++ b/ash/wm/desks/desks_unittests.cc
@@ -26,6 +26,7 @@ #include "ash/wm/overview/overview_grid.h" #include "ash/wm/overview/overview_item.h" #include "ash/wm/overview/overview_session.h" +#include "ash/wm/splitview/split_view_controller.h" #include "ash/wm/splitview/split_view_drag_indicators.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/window_state.h" @@ -170,6 +171,19 @@ return layout_manager->backdrop_controller(); } +// Returns true if |win1| is stacked (not directly) below |win2|. +bool IsStackedBelow(aura::Window* win1, aura::Window* win2) { + DCHECK_NE(win1, win2); + DCHECK_EQ(win1->parent(), win2->parent()); + + const auto& children = win1->parent()->children(); + auto win1_iter = std::find(children.begin(), children.end(), win1); + auto win2_iter = std::find(children.begin(), children.end(), win2); + DCHECK(win1_iter != children.end()); + DCHECK(win2_iter != children.end()); + return win1_iter < win2_iter; +} + // Defines an observer to test DesksController notifications. class TestObserver : public DesksController::Observer { public: @@ -1270,7 +1284,26 @@ EXPECT_EQ(nullptr, wm::GetActiveWindow()); } -TEST_F(DesksTest, TabletModeBackdrops) { +class TabletModeDesksTest : public DesksTest { + public: + TabletModeDesksTest() = default; + ~TabletModeDesksTest() override = default; + + // DesksTest: + void SetUp() override { + DesksTest::SetUp(); + + // Enter tablet mode. Avoid TabletModeController::OnGetSwitchStates() from + // disabling tablet mode. + base::RunLoop().RunUntilIdle(); + Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TabletModeDesksTest); +}; + +TEST_F(TabletModeDesksTest, Backdrops) { auto* controller = DesksController::Get(); controller->NewDesk(); ASSERT_EQ(2u, controller->desks().size()); @@ -1283,9 +1316,6 @@ // Enter tablet mode and expect that the backdrop is created only for desk_1, // since it's the one that has a window in it. - // Avoid TabletModeController::OnGetSwitchStates() from disabling tablet mode. - base::RunLoop().RunUntilIdle(); - Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); auto* desk_1_backdrop_controller = GetDeskBackdropController(desk_1, Shell::GetPrimaryRootWindow()); auto* desk_2_backdrop_controller = @@ -1347,7 +1377,7 @@ EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()); } -TEST_F(DesksTest, NoDesksBarInTabletModeWithOneDesk) { +TEST_F(TabletModeDesksTest, NoDesksBarInTabletModeWithOneDesk) { // Initially there's only one desk. auto* controller = DesksController::Get(); ASSERT_EQ(1u, controller->desks().size()); @@ -1356,11 +1386,7 @@ wm::ActivateWindow(window.get()); EXPECT_EQ(window.get(), wm::GetActiveWindow()); - // Enter tablet mode and expect that the DesksBar widget won't be created. - // Avoid TabletModeController::OnGetSwitchStates() from disabling tablet mode. - base::RunLoop().RunUntilIdle(); - Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); - + // Enter overview and expect that the DesksBar widget won't be created. auto* overview_controller = Shell::Get()->overview_controller(); overview_controller->StartOverview(); EXPECT_TRUE(overview_controller->InOverviewSession()); @@ -1389,24 +1415,15 @@ ASSERT_EQ(2u, desks_bar_view->mini_views().size()); } -TEST_F(DesksTest, TabletModeDesksCreationRemovalCycle) { +TEST_F(TabletModeDesksTest, DesksCreationRemovalCycle) { auto window = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); wm::ActivateWindow(window.get()); EXPECT_EQ(window.get(), wm::GetActiveWindow()); - // Enter tablet mode. Avoid TabletModeController::OnGetSwitchStates() from - // disabling tablet mode. - base::RunLoop().RunUntilIdle(); - Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); - - auto* overview_controller = Shell::Get()->overview_controller(); - overview_controller->StartOverview(); - EXPECT_TRUE(overview_controller->InOverviewSession()); - auto* desks_controller = DesksController::Get(); - // Create and remove desks in a cycle while in overview mode. Expect as the // containers are reused for new desks, their backdrop state are always // correct, and there are no crashes as desks are removed. + auto* desks_controller = DesksController::Get(); for (size_t i = 0; i < 2 * desks_util::kMaxNumberOfDesks; ++i) { desks_controller->NewDesk(); ASSERT_EQ(2u, desks_controller->desks().size()); @@ -1419,7 +1436,7 @@ { SCOPED_TRACE("Check backdrops after desk creation"); ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window()); - EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()->IsVisible()); + EXPECT_TRUE(desk_1_backdrop_controller->backdrop_window()->IsVisible()); EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()); } // Remove the active desk, and expect that now desk_2 should have a hidden @@ -1431,11 +1448,246 @@ EXPECT_TRUE(DoesActiveDeskContainWindow(window.get())); EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()); ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window()); - EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()->IsVisible()); + EXPECT_TRUE(desk_2_backdrop_controller->backdrop_window()->IsVisible()); } } } +TEST_F(TabletModeDesksTest, RestoreSplitViewOnDeskSwitch) { + // Create two desks with two snapped windows in each. + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + Desk* desk_1 = desks_controller->desks()[0].get(); + Desk* desk_2 = desks_controller->desks()[1].get(); + + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win2.get(), SplitViewController::RIGHT); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + + // Desk 2 has no windows, so the SplitViewController should be tracking no + // windows. + ActivateDesk(desk_2); + EXPECT_EQ(nullptr, split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + // However, the snapped windows on desk 1 should retain their snapped state. + EXPECT_TRUE(wm::GetWindowState(win1.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win2.get())->IsSnapped()); + + // Snap two other windows in desk 2. + auto win3 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win4 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + split_view_controller->SnapWindow(win3.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win4.get(), SplitViewController::RIGHT); + EXPECT_EQ(win3.get(), split_view_controller->left_window()); + EXPECT_EQ(win4.get(), split_view_controller->right_window()); + + // Switch back to desk 1, and expect the snapped windows are restored. + ActivateDesk(desk_1); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + EXPECT_TRUE(wm::GetWindowState(win3.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win4.get())->IsSnapped()); +} + +TEST_F(TabletModeDesksTest, SnappedStateRetainedOnSwitchingDesksFromOverview) { + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win2.get(), SplitViewController::RIGHT); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + + // Enter overview and switch to desk_2 using its mini_view. Overview should + // end, but TabletModeWindowManager should not maximize the snapped windows + // and they should retain their snapped state. + auto* overview_controller = Shell::Get()->overview_controller(); + overview_controller->StartOverview(); + EXPECT_TRUE(overview_controller->InOverviewSession()); + auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow()); + auto* desks_bar_view = overview_grid->desks_bar_view(); + auto* mini_view = desks_bar_view->mini_views()[1].get(); + Desk* desk_2 = desks_controller->desks()[1].get(); + EXPECT_EQ(desk_2, mini_view->desk()); + { + DeskSwitchAnimationWaiter waiter; + ClickOnMiniView(mini_view, GetEventGenerator()); + waiter.Wait(); + } + EXPECT_TRUE(wm::GetWindowState(win1.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win2.get())->IsSnapped()); + EXPECT_FALSE(overview_controller->InOverviewSession()); + + // Snap two other windows in desk_2, and switch back to desk_1 from overview. + // The snapped state should be retained for windows in both source and + // destination desks. + auto win3 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win4 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + split_view_controller->SnapWindow(win3.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win4.get(), SplitViewController::RIGHT); + EXPECT_EQ(win3.get(), split_view_controller->left_window()); + EXPECT_EQ(win4.get(), split_view_controller->right_window()); + overview_controller->StartOverview(); + EXPECT_TRUE(overview_controller->InOverviewSession()); + overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow()); + desks_bar_view = overview_grid->desks_bar_view(); + mini_view = desks_bar_view->mini_views()[0].get(); + Desk* desk_1 = desks_controller->desks()[0].get(); + EXPECT_EQ(desk_1, mini_view->desk()); + { + DeskSwitchAnimationWaiter waiter; + ClickOnMiniView(mini_view, GetEventGenerator()); + waiter.Wait(); + } + EXPECT_TRUE(wm::GetWindowState(win1.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win2.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win3.get())->IsSnapped()); + EXPECT_TRUE(wm::GetWindowState(win4.get())->IsSnapped()); + EXPECT_FALSE(overview_controller->InOverviewSession()); +} + +TEST_F(TabletModeDesksTest, OverviewStateOnSwitchToDeskWithSplitView) { + // Setup two desks, one (desk_1) with two snapped windows, and the other + // (desk_2) with only one snapped window. + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + Desk* desk_1 = desks_controller->desks()[0].get(); + Desk* desk_2 = desks_controller->desks()[1].get(); + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win2.get(), SplitViewController::RIGHT); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + auto* overview_controller = Shell::Get()->overview_controller(); + EXPECT_FALSE(overview_controller->InOverviewSession()); + ActivateDesk(desk_2); + EXPECT_FALSE(overview_controller->InOverviewSession()); + auto win3 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + split_view_controller->SnapWindow(win3.get(), SplitViewController::LEFT); + EXPECT_EQ(win3.get(), split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + + // Switching to the desk that has only one snapped window to be restored in + // SplitView should enter overview mode, whereas switching to one that has two + // snapped windows should exit overview. + ActivateDesk(desk_1); + EXPECT_FALSE(overview_controller->InOverviewSession()); + ActivateDesk(desk_2); + EXPECT_TRUE(overview_controller->InOverviewSession()); + ActivateDesk(desk_1); + EXPECT_FALSE(overview_controller->InOverviewSession()); +} + +TEST_F(TabletModeDesksTest, RemovingDesksWithSplitView) { + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + Desk* desk_2 = desks_controller->desks()[1].get(); + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + ActivateDesk(desk_2); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + split_view_controller->SnapWindow(win2.get(), SplitViewController::RIGHT); + EXPECT_EQ(nullptr, split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + + // Removing desk_2 will cause both snapped windows to merge in SplitView. + desks_controller->RemoveDesk(desk_2); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(win2.get(), split_view_controller->right_window()); + EXPECT_EQ(SplitViewState::kBothSnapped, split_view_controller->state()); +} + +TEST_F(TabletModeDesksTest, RemoveDeskWithMaximizedWindowAndMergeWithSnapped) { + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + Desk* desk_2 = desks_controller->desks()[1].get(); + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + ActivateDesk(desk_2); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + EXPECT_EQ(nullptr, split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + EXPECT_TRUE(wm::GetWindowState(win2.get())->IsMaximized()); + + // Removing desk_2 will cause us to enter overview mode without any crashes. + // SplitView will remain left snapped. + desks_controller->RemoveDesk(desk_2); + EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); + EXPECT_EQ(win1.get(), split_view_controller->left_window()); + EXPECT_EQ(nullptr, split_view_controller->right_window()); + EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state()); +} + +TEST_F(TabletModeDesksTest, BackdropsStacking) { + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(); + ASSERT_EQ(2u, desks_controller->desks().size()); + Desk* desk_1 = desks_controller->desks()[0].get(); + Desk* desk_2 = desks_controller->desks()[1].get(); + + auto win1 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win2 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + split_view_controller->SnapWindow(win1.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win2.get(), SplitViewController::RIGHT); + auto* desk_1_backdrop_controller = + GetDeskBackdropController(desk_1, Shell::GetPrimaryRootWindow()); + auto* desk_2_backdrop_controller = + GetDeskBackdropController(desk_2, Shell::GetPrimaryRootWindow()); + ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window()); + EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()); + + // The backdrop window should be stacked below both snapped windows. + auto* desk_1_backdrop = desk_1_backdrop_controller->backdrop_window(); + EXPECT_TRUE(IsStackedBelow(desk_1_backdrop, win1.get())); + EXPECT_TRUE(IsStackedBelow(desk_1_backdrop, win2.get())); + + // Switching to another desk doesn't change the backdrop state of the inactive + // desk. + ActivateDesk(desk_2); + ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window()); + EXPECT_FALSE(desk_2_backdrop_controller->backdrop_window()); + EXPECT_TRUE(IsStackedBelow(desk_1_backdrop, win1.get())); + EXPECT_TRUE(IsStackedBelow(desk_1_backdrop, win2.get())); + + // Snapping new windows in desk_2 should update the backdrop state of desk_2, + // but should not affect desk_1. + auto win3 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + auto win4 = CreateTestWindow(gfx::Rect(0, 0, 250, 100)); + split_view_controller->SnapWindow(win3.get(), SplitViewController::LEFT); + split_view_controller->SnapWindow(win4.get(), SplitViewController::RIGHT); + ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window()); + ASSERT_TRUE(desk_2_backdrop_controller->backdrop_window()); + auto* desk_2_backdrop = desk_2_backdrop_controller->backdrop_window(); + EXPECT_TRUE(IsStackedBelow(desk_2_backdrop, win3.get())); + EXPECT_TRUE(IsStackedBelow(desk_2_backdrop, win4.get())); +} + TEST_F(DesksTest, MiniViewsTouchGestures) { auto* controller = DesksController::Get(); controller->NewDesk();
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc index d7ccaa5..cb572a8 100644 --- a/ash/wm/overview/overview_controller.cc +++ b/ash/wm/overview/overview_controller.cc
@@ -563,12 +563,14 @@ if (InOverviewSession()) { // Do not allow ending overview if we're in single split mode unless swiping - // up from the shelf in tablet mode. + // up from the shelf in tablet mode, or ending overview immediately without + // animations. if (windows.empty() && Shell::Get()->split_view_controller()->InTabletSplitViewMode() && Shell::Get()->split_view_controller()->state() != SplitViewState::kBothSnapped && - type != OverviewSession::EnterExitOverviewType::kSwipeFromShelf) { + type != OverviewSession::EnterExitOverviewType::kSwipeFromShelf && + type != OverviewSession::EnterExitOverviewType::kImmediateExit) { return true; }
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h index a35e2fe..7d09b04c 100644 --- a/ash/wm/splitview/split_view_controller.h +++ b/ash/wm/splitview/split_view_controller.h
@@ -70,6 +70,9 @@ // See crbug.com/950827. kPipExpanded, kExitTabletMode, + // Splitview is being ended due to a change in Virtual Desks, such as + // switching desks or removing a desk. + kDesksChange, }; // The behaviors of split view are very different when in tablet mode and in
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc index 30ef609..7f2806c 100644 --- a/ash/wm/splitview/split_view_divider.cc +++ b/ash/wm/splitview/split_view_divider.cc
@@ -8,6 +8,7 @@ #include "ash/display/screen_orientation_controller.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/window_properties.h" #include "ash/screen_util.h" #include "ash/shell.h" #include "ash/wm/desks/desks_util.h" @@ -445,6 +446,8 @@ DividerView* divider_view = new DividerView(this); divider_widget_->set_focus_on_creation(false); divider_widget_->Init(params); + aura::Window* widget_window = divider_widget_->GetNativeWindow(); + widget_window->SetProperty(kHideInDeskMiniViewKey, true); divider_widget_->SetVisibilityAnimationTransition( views::Widget::ANIMATE_NONE); divider_widget_->SetContentsView(divider_view);
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc index c0b969a..7254a01 100644 --- a/ash/wm/splitview/split_view_utils.cc +++ b/ash/wm/splitview/split_view_utils.cc
@@ -13,6 +13,8 @@ #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/toast/toast_manager_impl.h" +#include "ash/wm/mru_window_tracker.h" +#include "ash/wm/overview/overview_controller.h" #include "ash/wm/screen_pinning_controller.h" #include "ash/wm/splitview/split_view_constants.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" @@ -227,6 +229,66 @@ layer->SetTransform(target_transform); } +void MaybeRestoreSplitView(bool refresh_snapped_windows) { + if (!ShouldAllowSplitView()) + return; + + // Search for snapped windows to detect if the now active user session, or + // desk were in split view. In case multiple windows were snapped to one side, + // one window after another, there may be multiple windows in a LEFT_SNAPPED + // state or multiple windows in a RIGHT_SNAPPED state. For each of those two + // state types that belongs to multiple windows, the relevant window will be + // listed first among those windows, and a null check in the loop body below + // will filter out the rest of them. + // TODO(amusbach): The windows that were in split view may have later been + // destroyed or changed to non-snapped states. Then the following for loop + // could snap windows that were not in split view. Also, a window may have + // become full screen, and if so, then it would be better not to reactivate + // split view. See https://crbug.com/944134. + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + + if (refresh_snapped_windows) { + const MruWindowTracker::WindowList windows = + Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal( + kActiveDesk); + for (aura::Window* window : windows) { + switch (wm::GetWindowState(window)->GetStateType()) { + case WindowStateType::kLeftSnapped: + if (!split_view_controller->left_window()) { + split_view_controller->SnapWindow(window, + SplitViewController::LEFT); + } + break; + + case WindowStateType::kRightSnapped: + if (!split_view_controller->right_window()) { + split_view_controller->SnapWindow(window, + SplitViewController::RIGHT); + } + break; + + default: + break; + } + + if (split_view_controller->state() == SplitViewState::kBothSnapped) + break; + } + } + + // Ensure that overview mode is active if and only if there is a window + // snapped to one side but no window snapped to the other side. + OverviewController* overview_controller = Shell::Get()->overview_controller(); + SplitViewState state = split_view_controller->state(); + if (state == SplitViewState::kLeftSnapped || + state == SplitViewState::kRightSnapped) { + overview_controller->StartOverview(); + } else { + overview_controller->EndOverview(); + } +} + bool IsClamshellSplitViewModeEnabled() { return base::FeatureList::IsEnabled( ash::features::kDragToSnapInClamshellMode);
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h index 5547e81..0cd0552 100644 --- a/ash/wm/splitview/split_view_utils.h +++ b/ash/wm/splitview/split_view_utils.h
@@ -80,6 +80,11 @@ SplitviewAnimationType type, const gfx::Transform& target_transform); +// Restores split view and overview based on the current split view's state. +// If |refresh_snapped_windows| is true, it will update the left and right +// snapped windows based on the MRU windows snapped states. +void MaybeRestoreSplitView(bool refresh_snapped_windows); + // Returns true if we allow dragging an overview window to snap to split view in // clamshell mode. ASH_EXPORT bool IsClamshellSplitViewModeEnabled();
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc index dd70645..840f54a 100644 --- a/ash/wm/tablet_mode/tablet_mode_controller.cc +++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -705,8 +705,11 @@ tablet_mode_window_manager_->SetIgnoreWmEventsForExit(); for (auto& observer : tablet_mode_observers_) observer.OnTabletModeEnding(); - tablet_mode_window_manager_->Shutdown(); - tablet_mode_window_manager_.reset(); + // Make sure that calling `TabletModeController::InTabletMode()` returns + // false from now on. + std::unique_ptr<TabletModeWindowManager> to_be_removed_tablet_mode_wm = + std::move(tablet_mode_window_manager_); + to_be_removed_tablet_mode_wm->Shutdown(); base::RecordAction(base::UserMetricsAction("Touchview_Disabled")); RecordTabletModeUsageInterval(TABLET_MODE_INTERVAL_ACTIVE); state_ = State::kInClamshellMode;
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc index 78a4b3c7..e92a82e 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -65,7 +65,7 @@ // they are eligible to be carried over to splitscreen. A window must meet // IsCarryOverCandidateForSplitView() to be carried over to splitscreen. MruWindowTracker::WindowList mru_windows = - Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks); + Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk); if (IsCarryOverCandidateForSplitView(mru_windows, 0u)) { if (wm::GetWindowState(mru_windows[0])->GetStateType() == WindowStateType::kLeftSnapped) { @@ -233,7 +233,14 @@ } void TabletModeWindowManager::Init() { - // There are 3 cases when entering tablet mode: + // If clamshell split view mode is not enabled, still keep the old behavior: + // end overview if it's active. And carry over snapped windows to splitscreen + // if possible. + if (!IsClamshellSplitViewModeEnabled()) + Shell::Get()->overview_controller()->EndOverview(); + + // If clamshell splitview mode is enabled, there are 3 cases when entering + // tablet mode: // 1) overview is active but split view is inactive: keep overview active in // tablet mode. // 2) overview and splitview are both active (splitview can only be active @@ -263,38 +270,49 @@ } void TabletModeWindowManager::Shutdown() { - // There are 4 cases when exiting tablet mode: - // 1) overview is active but split view is inactive: keep overview active in - // clamshell mode. - // 2) overview and splitview are both active: keep overview and splitview both - // active in clamshell mode, unless if it's single split state, splitview - // and overview will both be ended. - // 3) overview is inactive but split view is active (two snapped windows): - // split view is no longer active. But the two snapped windows will still - // keep snapped in clamshell mode. - // 4) overview and splitview are both inactive: keep the current behavior, - // i.e., restore all windows to its window state before entering tablet - // mode. + base::flat_map<aura::Window*, WindowStateType> carryover_windows_in_splitview; + const bool was_in_overview = + Shell::Get()->overview_controller()->InOverviewSession(); + // If clamshell split view mode is not enabled, still keep the old behavior: + // End overview if overview is active and restore all windows' window states + // to their previous window states. + if (!IsClamshellSplitViewModeEnabled()) { + Shell::Get()->overview_controller()->EndOverview(); + } else { + // If clamshell split view mode is enabled, there are 4 cases when exiting + // tablet mode: + // 1) overview is active but split view is inactive: keep overview active in + // clamshell mode. + // 2) overview and splitview are both active: keep overview and splitview + // both + // active in clamshell mode, unless if it's single split state, splitview + // and overview will both be ended. + // 3) overview is inactive but split view is active (two snapped windows): + // split view is no longer active. But the two snapped windows will still + // keep snapped in clamshell mode. + // 4) overview and splitview are both inactive: keep the current behavior, + // i.e., restore all windows to its window state before entering tablet + // mode. - // TODO(xdai): Instead of caching snapped windows and their state here, we - // should try to see if it can be done in the WindowState::State impl. - base::flat_map<aura::Window*, WindowStateType> windows_in_splitview = - GetCarryOverWindowsInSplitView(); + // TODO(xdai): Instead of caching snapped windows and their state here, we + // should try to see if it can be done in the WindowState::State impl. + carryover_windows_in_splitview = GetCarryOverWindowsInSplitView(); - // For case 2 and 3: End splitview mode for two snapped windows case or single - // split case to match the clamshell split view behavior. (there is no both - // snapped state or single split state in clamshell split view). The windows - // will still be kept snapped though. - SplitViewController* split_view_controller = - Shell::Get()->split_view_controller(); - if (split_view_controller->InSplitViewMode()) { - OverviewController* overview_controller = - Shell::Get()->overview_controller(); - if (!overview_controller->InOverviewSession() || - overview_controller->overview_session()->IsEmpty()) { - Shell::Get()->split_view_controller()->EndSplitView( - SplitViewController::EndReason::kExitTabletMode); - overview_controller->EndOverview(); + // For case 2 and 3: End splitview mode for two snapped windows case or + // single split case to match the clamshell split view behavior. (there is + // no both snapped state or single split state in clamshell split view). The + // windows will still be kept snapped though. + SplitViewController* split_view_controller = + Shell::Get()->split_view_controller(); + if (split_view_controller->InSplitViewMode()) { + OverviewController* overview_controller = + Shell::Get()->overview_controller(); + if (!overview_controller->InOverviewSession() || + overview_controller->overview_session()->IsEmpty()) { + Shell::Get()->split_view_controller()->EndSplitView( + SplitViewController::EndReason::kExitTabletMode); + overview_controller->EndOverview(); + } } } @@ -309,7 +327,8 @@ ScopedObserveWindowAnimation scoped_observe(GetTopWindow(), this, /*exiting_tablet_mode=*/true); - ArrangeWindowsForClamshellMode(windows_in_splitview); + ArrangeWindowsForClamshellMode(carryover_windows_in_splitview, + was_in_overview); } int TabletModeWindowManager::GetNumberOfManagedWindows() { @@ -363,8 +382,10 @@ // Maximize all snapped windows upon exiting overview mode except snapped // windows in splitview mode. Note the snapped window might not be tracked in // our |window_state_map_|. - MruWindowTracker::WindowList windows = - Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(kAllDesks); + // Leave snapped windows on inactive desks unchanged. + const MruWindowTracker::WindowList windows = + Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal( + kActiveDesk); for (auto* window : windows) { if (split_view_controller->left_window() != window && split_view_controller->right_window() != window) { @@ -384,6 +405,7 @@ case SplitViewController::EndReason::kActiveUserChanged: case SplitViewController::EndReason::kWindowDragStarted: case SplitViewController::EndReason::kExitTabletMode: + case SplitViewController::EndReason::kDesksChange: // For the case of kHomeLauncherPressed, the home launcher will minimize // the snapped windows after ending splitview, so avoid maximizing them // here. For the case of kActiveUserChanged, the snapped windows will be @@ -395,8 +417,10 @@ // Maximize all snapped windows upon exiting split view mode. Note the snapped // window might not be tracked in our |window_state_map_|. - MruWindowTracker::WindowList windows = - Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(kAllDesks); + // Leave snapped windows on inactive desks unchanged. + const MruWindowTracker::WindowList windows = + Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal( + kActiveDesk); for (auto* window : windows) MaximizeIfSnapped(window); } @@ -522,6 +546,7 @@ // If a user session is now active for the first time since clamshell mode, // then do the logic for carrying over snapped windows. Else recreate the // split view layout from the last time the current user session was active. + bool refresh_snapped_windows = false; if (accounts_since_entering_tablet_.count(account_id) == 0u) { base::flat_map<aura::Window*, WindowStateType> windows_in_splitview = GetCarryOverWindowsInSplitView(); @@ -530,53 +555,10 @@ DoSplitViewTransition(windows_in_splitview, divider_position); accounts_since_entering_tablet_.insert(account_id); } else { - // Search for snapped windows to detect if the now active user session was - // in split view. In case multiple windows were snapped to one side, one - // window after another, there may be multiple windows in a LEFT_SNAPPED - // state or multiple windows in a RIGHT_SNAPPED state. For each of those two - // state types that belongs to multiple windows, the relevant window will be - // listed first among those windows, and a null check in the loop body below - // will filter out the rest of them. - // TODO(amusbach): The windows that were in split view may have later been - // destroyed or changed to non-snapped states. Then the following for loop - // could snap windows that were not in split view. Also, a window may have - // become full screen, and if so, then it would be better not to reactivate - // split view. See https://crbug.com/944134. - MruWindowTracker::WindowList windows = - Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal( - kAllDesks); - for (aura::Window* window : windows) { - switch (wm::GetWindowState(window)->GetStateType()) { - case WindowStateType::kLeftSnapped: - if (split_view_controller->left_window() == nullptr) { - split_view_controller->SnapWindow(window, - SplitViewController::LEFT); - } - break; - case WindowStateType::kRightSnapped: - if (split_view_controller->right_window() == nullptr) { - split_view_controller->SnapWindow(window, - SplitViewController::RIGHT); - } - break; - default: - break; - } - if (split_view_controller->state() == SplitViewState::kBothSnapped) - break; - } + refresh_snapped_windows = true; } - // Ensure that overview mode is active if and only if there is a window - // snapped to one side but no window snapped to the other side. - OverviewController* overview_controller = Shell::Get()->overview_controller(); - SplitViewState state = split_view_controller->state(); - if (state == SplitViewState::kLeftSnapped || - state == SplitViewState::kRightSnapped) { - overview_controller->StartOverview(); - } else { - overview_controller->EndOverview(); - } + MaybeRestoreSplitView(refresh_snapped_windows); } WindowStateType TabletModeWindowManager::GetDesktopWindowStateType( @@ -588,11 +570,8 @@ } void TabletModeWindowManager::ArrangeWindowsForTabletMode() { - // |split_view_eligible_windows| is for determining split view layout. // |activatable_windows| includes all windows to be tracked, and that includes // windows on the lock screen via |scoped_skip_user_session_blocked_check|. - MruWindowTracker::WindowList split_view_eligible_windows = - Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks); ScopedSkipUserSessionBlockedCheck scoped_skip_user_session_blocked_check; MruWindowTracker::WindowList activatable_windows = Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal(kAllDesks); @@ -629,12 +608,13 @@ } void TabletModeWindowManager::ArrangeWindowsForClamshellMode( - base::flat_map<aura::Window*, WindowStateType> windows_in_splitview) { + base::flat_map<aura::Window*, WindowStateType> windows_in_splitview, + bool was_in_overview) { int divider_position = CalculateCarryOverDividerPostion(windows_in_splitview); while (window_state_map_.size()) { aura::Window* window = window_state_map_.begin()->first; - ForgetWindow(window, /*destroyed=*/false); + ForgetWindow(window, /*destroyed=*/false, was_in_overview); } if (IsClamshellSplitViewModeEnabled()) { @@ -665,7 +645,8 @@ } void TabletModeWindowManager::ForgetWindow(aura::Window* window, - bool destroyed) { + bool destroyed, + bool was_in_overview) { added_windows_.erase(window); window->RemoveObserver(this); @@ -686,7 +667,7 @@ } else { // By telling the state object to revert, it will switch back the old // State object and destroy itself, calling WindowStateDestroyed(). - it->second->LeaveTabletMode(wm::GetWindowState(it->first)); + it->second->LeaveTabletMode(wm::GetWindowState(it->first), was_in_overview); DCHECK(!IsTrackingWindow(window)); } }
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h index ce242d2..9e391f0 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_manager.h +++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -114,15 +114,18 @@ // |window| is not yet tracked, returns the current state type of |window|. WindowStateType GetDesktopWindowStateType(aura::Window* window) const; - // Maximize all windows, except that snapped windows shall carry over to split - // view as determined by GetSnapPositions(). + // Maximizes all windows, except that snapped windows shall carry over to + // split view as determined by GetCarryOverWindowsInSplitView(). void ArrangeWindowsForTabletMode(); - // Revert all windows to how they were arranged before tablet mode. + // Reverts all windows to how they were arranged before tablet mode. // |windows_in_splitview| contains the windows that were in splitview before - // entering clamshell mode. + // entering clamshell mode, and if clamshell split view is enabled, these + // windows will be carried over to clamshell split view. |was_in_overview| + // indicates whether overview is active before entering clamshell mode. void ArrangeWindowsForClamshellMode( - base::flat_map<aura::Window*, WindowStateType> windows_in_splitview); + base::flat_map<aura::Window*, WindowStateType> windows_in_splitview, + bool was_in_overview); // If the given window should be handled by us, this function will add it to // the list of known windows (remembering the initial show state). @@ -133,10 +136,13 @@ bool snap = false, bool animate_bounds_on_attach = true); - // Remove a window from our tracking list. If the window is going to be - // destroyed, do not restore its old previous window state object as it will - // send unnecessary window state change event. - void ForgetWindow(aura::Window* window, bool destroyed); + // Removes a window from our tracking list. |was_in_overview| used when + // |destroyed| is false to help handle leaving tablet mode. If the window is + // going to be destroyed, do not restore its old previous window state object + // as it will send unnecessary window state change event. + void ForgetWindow(aura::Window* window, + bool destroyed, + bool was_in_overview = false); // Returns true when the given window should be modified in any way by us. bool ShouldHandleWindow(aura::Window* window);
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc index ca1e6243..bf92497 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -1801,24 +1801,22 @@ std::unique_ptr<aura::Window> window( CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect)); - // 1. Clamshell -> tablet. If overview is active, it should still be kept - // active after transition. + // 1. Clamshell -> tablet. If overview is active, it should be ended after + // transition since clamshell splitview is not enabled. OverviewController* overview_controller = Shell::Get()->overview_controller(); EXPECT_TRUE(overview_controller->StartOverview()); EXPECT_TRUE(overview_controller->InOverviewSession()); TabletModeWindowManager* manager = CreateTabletModeWindowManager(); EXPECT_TRUE(manager); - EXPECT_TRUE(overview_controller->InOverviewSession()); + EXPECT_FALSE(overview_controller->InOverviewSession()); - // 2. Tablet -> Clamshell. If overview is active, it should still be kept - // active after transition. + // 2. Tablet -> Clamshell. If overview is active, it should be ended after + // transition. DestroyTabletModeWindowManager(); - EXPECT_TRUE(overview_controller->InOverviewSession()); + EXPECT_FALSE(overview_controller->InOverviewSession()); // 3. Clamshell -> tablet. If overview is inactive, it should still be kept // inactive after transition. All windows will be maximized. - EXPECT_TRUE(overview_controller->EndOverview()); - EXPECT_FALSE(overview_controller->InOverviewSession()); CreateTabletModeWindowManager(); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized());
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc index 5b8263a..2825e6c 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_state.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -6,12 +6,14 @@ #include <utility> +#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_animation_types.h" #include "ash/public/cpp/window_properties.h" #include "ash/public/cpp/window_state_type.h" #include "ash/screen_util.h" #include "ash/shell.h" +#include "ash/wm/desks/desks_controller.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/screen_pinning_controller.h" #include "ash/wm/splitview/split_view_controller.h" @@ -159,6 +161,11 @@ return window == TabletModeWindowManager::GetTopWindow(); } +bool IsSnapped(WindowStateType state) { + return state == WindowStateType::kLeftSnapped || + state == WindowStateType::kRightSnapped; +} + } // namespace // static @@ -198,12 +205,15 @@ creator_->WindowStateDestroyed(window_); } -void TabletModeWindowState::LeaveTabletMode(wm::WindowState* window_state) { - // Only do bounds change animation if the window is the top window or a window - // showing in splitview, and the window has changed its state. Otherwise, - // restore its bounds immediately. +void TabletModeWindowState::LeaveTabletMode(wm::WindowState* window_state, + bool was_in_overview) { + // Only do bounds change animation if the window was showing in overview, + // or the top window or a window showing in splitview before leaving tablet + // mode, and the window has changed its state. Otherwise, restore its bounds + // immediately. EnterAnimationType animation_type = - window_state->IsSnapped() || IsTopWindow(window_state->window()) + was_in_overview || window_state->IsSnapped() || + IsTopWindow(window_state->window()) ? DEFAULT : IMMEDIATE; if (old_state_->GetType() == window_state->GetStateType() && @@ -324,9 +334,13 @@ if (current_state_type_ != WindowStateType::kMaximized && current_state_type_ != WindowStateType::kFullscreen && current_state_type_ != WindowStateType::kMinimized) { - WindowStateType new_state = - GetMaximizedOrCenteredWindowType(window_state); - UpdateWindow(window_state, new_state, true /* animated */); + // If an already snapped window gets added to the workspace it should + // not be maximized, rather retain its previous state. + const WindowStateType new_state = + IsSnapped(current_state_type_) + ? window_state->GetStateType() + : GetMaximizedOrCenteredWindowType(window_state); + UpdateWindow(window_state, new_state, /*animated=*/true); } break; case wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED:
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.h b/ash/wm/tablet_mode/tablet_mode_window_state.h index d4b9494..edbe9b99 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_state.h +++ b/ash/wm/tablet_mode/tablet_mode_window_state.h
@@ -41,7 +41,7 @@ void set_ignore_wm_events(bool ignore) { ignore_wm_events_ = ignore; } // Leaves the tablet mode by reverting to previous state object. - void LeaveTabletMode(wm::WindowState* window_state); + void LeaveTabletMode(wm::WindowState* window_state, bool was_in_overview); // WindowState::State overrides: void OnWMEvent(wm::WindowState* window_state,
diff --git a/base/BUILD.gn b/base/BUILD.gn index 1985d961..d65b96f 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1574,6 +1574,7 @@ deps += [ "//third_party/fuchsia-sdk/sdk:async_default", "//third_party/fuchsia-sdk/sdk:async_loop_cpp", + "//third_party/fuchsia-sdk/sdk:deprecatedtimezone", "//third_party/fuchsia-sdk/sdk:fidl", "//third_party/fuchsia-sdk/sdk:svc", "//third_party/fuchsia-sdk/sdk:sys",
diff --git a/base/metrics/ukm_source_id.h b/base/metrics/ukm_source_id.h index 6b259c7..51f3e11 100644 --- a/base/metrics/ukm_source_id.h +++ b/base/metrics/ukm_source_id.h
@@ -17,11 +17,24 @@ class BASE_EXPORT UkmSourceId { public: enum class Type : int64_t { + // Source ids of this type are created via ukm::AssignNewSourceId, to denote + // 'custom' source other than the 3 types below. Source of this type has + // additional restrictions with logging, as determined by + // IsWhitelistedSourceId. UKM = 0, + // Sources created by navigation. They will be kept in memory as long as + // the associated tab is still alive and the number of sources are within + // the max threshold. NAVIGATION_ID = 1, + // Source ID used by AppLaunchEventLogger::Log. A new source of this type + // and associated events are expected to be recorded within the same report + // interval; it will not be kept in memory between different reports. APP_ID = 2, // Source ID for background events that don't have an open tab but the - // associated URL is still present in the browser's history. + // associated URL is still present in the browser's history. A new source of + // this type and associated events are expected to be recorded within the + // same report interval; it will not be kept in memory between different + // reports. HISTORY_ID = 3, };
diff --git a/base/time/time_exploded_posix.cc b/base/time/time_exploded_posix.cc index a80d4cb..0655703 100644 --- a/base/time/time_exploded_posix.cc +++ b/base/time/time_exploded_posix.cc
@@ -24,18 +24,28 @@ #include "base/os_compat_nacl.h" #endif +#if defined(OS_FUCHSIA) +#include <fuchsia/deprecatedtimezone/cpp/fidl.h> +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" +#include "base/no_destructor.h" +#include "base/numerics/clamped_math.h" +#endif + #if defined(OS_MACOSX) static_assert(sizeof(time_t) >= 8, "Y2038 problem!"); #endif namespace { +#if !defined(OS_FUCHSIA) // This prevents a crash on traversing the environment global and looking up // the 'TZ' variable in libc. See: crbug.com/390567. base::Lock* GetSysTimeToTimeStructLock() { static auto* lock = new base::Lock(); return lock; } +#endif // !defined(OS_FUCHSIA) // Define a system-specific SysTime that wraps either to a time_t or // a time64_t depending on the host system, and associated convertion. @@ -59,6 +69,70 @@ gmtime64_r(&t, timestruct); } +#elif defined(OS_FUCHSIA) +typedef time_t SysTime; + +SysTime GetTimezoneOffset(SysTime utc_time) { + static base::NoDestructor<fuchsia::deprecatedtimezone::TimezoneSyncPtr> + timezone( + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() + ->ConnectToServiceSync<fuchsia::deprecatedtimezone::Timezone>()); + + int64_t milliseconds_since_epoch = + base::ClampMul(utc_time, base::Time::kMillisecondsPerSecond); + int32_t local_offset_minutes = 0; + int32_t dst_offset_minutes = 0; + zx_status_t status = (*timezone.get()) + ->GetTimezoneOffsetMinutes(milliseconds_since_epoch, + &local_offset_minutes, + &dst_offset_minutes); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) << "Failed to get current timezone offset."; + return 0; + } + return (local_offset_minutes + dst_offset_minutes) * + base::Time::kSecondsPerMinute; +} + +SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { + SysTime result = timegm(timestruct); + if (is_local) { + // Local->UTC conversion may be ambiguous, particularly when local clock is + // changed back (e.g. in when DST ends). In such cases there are 2 correct + // results and this function will return one of them. Also some local time + // values may be invalid. Specifically when local time is rolled forward + // (when DST starts) the values in the transitional period are invalid and + // don't have corresponding values in the UTC timeline. In those cases using + // timezone offset either before or after transition is acceptable. + // + // fuchsia::deprecatedtimezone API returns offset based on UTC time. It may + // return incorrect result when called with a value that also includes + // timezone offset. Particularly this is a problem when the time is close to + // DST transitions. For example, when transitioning from PST (UTC-8, + // non-DST) to PDT (UTC-7, DST) GetTimezoneOffset(local_time) will return a + // value that's off by 1 hour for 8 hours after the transition. To avoid + // this problem the offset is estimated as GetTimezoneOffset(local_time) + // from which |approx_utc_time| is calculated. Then + // GetTimezoneOffset(approx_utc_time) is used to calculate the actual + // offset. This works correctly assuming timezone transition can happen at + // most once per day. When both before and after offsets are in the [-1H, + // 1H] range then the |approx_utc_time| is correct (see the note above for + // definition of what is considered correct). Otherwise |approx_utc_time| + // may be off by 1 hour. In those cases GetTimezoneOffset(approx_utc_time) + // will return correct offset because we can assume there are no timezone + // changes in the [UTC-1H, UTC+1H] period (the transition is scheduled + // either before UTC-1H or after UTC+1H). + int64_t approx_utc_time = result - GetTimezoneOffset(result); + result -= GetTimezoneOffset(approx_utc_time); + } + return result; +} + +void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { + if (is_local) + t += GetTimezoneOffset(t); + gmtime_r(&t, timestruct); +} #elif defined(OS_AIX) // The function timegm is not available on AIX.
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h index 426860c..7c0723ff 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h
@@ -154,7 +154,6 @@ X(TRACE_DISABLED_BY_DEFAULT("cc.debug")) \ X(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf")) \ X(TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items")) \ - X(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc")) \ X(TRACE_DISABLED_BY_DEFAULT("cc.debug.picture")) \ X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler")) \ X(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames")) \
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py index 05ca865..f0f7a30 100755 --- a/build/android/gyp/proguard.py +++ b/build/android/gyp/proguard.py
@@ -7,6 +7,7 @@ import cStringIO import optparse import os +import re import shutil import sys import tempfile @@ -80,6 +81,10 @@ parser.add_option( '--repackage-classes', help='Unique package name given to an asynchronously proguarded module') + parser.add_option( + '--disable-outlining', + action='store_true', + help='Disable the outlining optimization provided by R8.') options, _ = parser.parse_args(args) @@ -212,18 +217,30 @@ f.close() print_stdout = '-whyareyoukeeping' in merged_configs + def run_r8(cmd): + stderr_filter = None + env = os.environ.copy() + if options.disable_outlining: + stderr_filter = lambda l: re.sub(r'.*_JAVA_OPTIONS.*\n?', '', l) + env['_JAVA_OPTIONS'] = '-Dcom.android.tools.r8.disableOutlining=1' + build_utils.CheckOutput( + cmd, + env=env, + print_stdout=print_stdout, + stderr_filter=stderr_filter) + if options.output_path.endswith('.dex'): with build_utils.TempDir() as tmp_dex_dir: cmd, temp_config_string = _CreateR8Command( options, tmp_mapping_path, tmp_dex_dir, tmp_proguard_config_path, libraries) - build_utils.CheckOutput(cmd, print_stdout=print_stdout) + run_r8(cmd) _MoveTempDexFile(tmp_dex_dir, options.output_path) else: cmd, temp_config_string = _CreateR8Command( options, tmp_mapping_path, options.output_path, tmp_proguard_config_path, libraries) - build_utils.CheckOutput(cmd, print_stdout=print_stdout) + run_r8(cmd) # Copy output files to correct locations. with build_utils.AtomicOutput(options.mapping_output) as mapping:
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 4bd53bb4..8798d2c2 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -954,6 +954,11 @@ "@FileArg($_rebased_build_config:android:sdk_jars)", ] + if (defined(invoker.disable_r8_outlining) && + invoker.disable_r8_outlining) { + args += [ "--disable-outlining" ] + } + if (defined(invoker.is_static_library) && invoker.is_static_library) { args += [ "--extra-mapping-output-paths", @@ -1123,6 +1128,7 @@ forward_variables_from(invoker, [ "build_config", + "disable_r8_outlining", "deps", "proguard_expectations_file", "proguard_jar_path",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index de322c7..6eead907 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2130,6 +2130,7 @@ # locale_config_java_packages: Optional list of java packages. If given, a # LocaleConfig.java file will be generated for each package, and will # contain the list of compressed and uncompressed locale pak files. + # disable_r8_outlining: Turn off outlining during the proguard step. template("android_apk_or_module") { forward_variables_from(invoker, [ "testonly" ]) assert(defined(invoker.final_apk_path) || defined(invoker.name)) @@ -2883,6 +2884,7 @@ dex(_final_dex_target_name) { forward_variables_from(invoker, [ + "disable_r8_outlining", "min_sdk_version", "dexlayout_profile", ]) @@ -3322,6 +3324,7 @@ "data_deps", "deps", "dexlayout_profile", + "disable_r8_outlining", "dist_ijar_path", "dont_load_shared_libraries", "enable_chromium_linker_tests",
diff --git a/build/config/fuchsia/tests.cmx b/build/config/fuchsia/tests.cmx index 174295d..539bc19 100644 --- a/build/config/fuchsia/tests.cmx +++ b/build/config/fuchsia/tests.cmx
@@ -13,6 +13,7 @@ ], "services": [ "fuchsia.device.NameProvider", + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.fonts.Provider", "fuchsia.logger.Log", "fuchsia.logger.LogSink",
diff --git a/build/config/mac/mac_sdk.gni b/build/config/mac/mac_sdk.gni index 5e59c2e2..be84c4d 100644 --- a/build/config/mac/mac_sdk.gni +++ b/build/config/mac/mac_sdk.gni
@@ -45,6 +45,15 @@ # iOS toolchains. _verify_sdk = is_chrome_branded && is_official_build && target_os != "ios" +# The path to the hermetic install of Xcode. Only relevant when +# use_system_xcode = false. +if (!use_system_xcode) { + _hermetic_xcode_path = + rebase_path("//build/${target_os}_files/xcode_binaries", + "", + root_build_dir) +} + find_sdk_args = [ "--print_sdk_path", "--print_bin_path", @@ -52,7 +61,7 @@ if (!use_system_xcode) { find_sdk_args += [ "--developer_dir", - hermetic_xcode_path, + _hermetic_xcode_path, ] } if (_verify_sdk) { @@ -86,7 +95,7 @@ if (!use_system_xcode) { sdk_info_args += [ "--developer_dir", - hermetic_xcode_path, + _hermetic_xcode_path, ] } sdk_info_args += [ mac_sdk_name ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index d4891e6..e7ab75c 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8907574929731684000 \ No newline at end of file +8907479771819418416 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 23ba05e..e471749c 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8907575853201394016 \ No newline at end of file +8907483844330972992 \ No newline at end of file
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py index aed58896..7e87522 100755 --- a/build/mac_toolchain.py +++ b/build/mac_toolchain.py
@@ -38,10 +38,10 @@ # xcode_binaries, see InstallXcodeBinaries. https://crbug.com/984746 MAC_TOOLCHAIN_VERSION = '9E501' -# This contains binaries from Xcode 9.3.1, along with the 10.13 SDK. -# To build this package, see comments in build/xcode_binaries.yaml +# This contains binaries from Xcode 10.12.1, along with the 10.13 and 10.14 +# SDKs. To build this package, see comments in build/xcode_binaries.yaml MAC_BINARIES_LABEL = 'infra_internal/ios/xcode/xcode_binaries/mac-amd64' -MAC_BINARIES_TAG = 'oXeIQqw4E0pXIStoixHwKg_1TJZTJGhfhmn2fq8LPOAC' +MAC_BINARIES_TAG = '4SpEve_8bN_DmTH0QdxMQoCTsrAe5QK04uoMEnNHAvQC' # The toolchain will not be downloaded if the minimum OS version is not met. # 17 is the major version number for macOS 10.13. @@ -195,7 +195,8 @@ return # Otherwise manually accept the license. This will prompt for sudo. - print('Accepting new Xcode license.') + print('Accepting new Xcode license. Requires sudo.') + sys.stdout.flush() args = ['sudo', 'defaults', 'write', current_license_path, 'IDEXcodeVersionForAgreedToGMLicense', cipd_xcode_version] subprocess.call(args)
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni index 7f2e19d..260ed0b7 100644 --- a/build/toolchain/toolchain.gni +++ b/build/toolchain/toolchain.gni
@@ -34,17 +34,6 @@ "Linker map files should only be generated for Android and Linux") } -# The path to the hermetic install of Xcode. Only relevant when -# use_system_xcode = false. -if (is_ios) { - hermetic_xcode_path = - rebase_path("//build/${target_os}_files/Xcode.app", "", root_build_dir) -} else { - hermetic_xcode_path = rebase_path("//build/${target_os}_files/xcode_binaries", - "", - root_build_dir) -} - declare_args() { if (is_clang) { # Clang compiler version. Clang files are placed at version-dependent paths.
diff --git a/buildtools/ensure_gn_version.py b/buildtools/ensure_gn_version.py index 4c40f7c6..cfc75a3 100755 --- a/buildtools/ensure_gn_version.py +++ b/buildtools/ensure_gn_version.py
@@ -83,10 +83,12 @@ out = '' if out: - current_revision = re.findall(r'\((.*)\)', out)[0] - if desired_revision.startswith(current_revision): - # We're on the right version, so we're done. - return 0 + current_revision_match = re.findall(r'\((.*)\)', out) + if current_revision_match: + current_revision = current_revision_match[0] + if desired_revision.startswith(current_revision): + # We're on the right version, so we're done. + return 0 print("`%s` returned '%s', which wasn't what we were expecting." % (cmd_str, out.strip()))
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc index 452fe33..02c6826 100644 --- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -173,8 +173,6 @@ DCHECK(frame.metadata.begin_frame_ack.has_damage); DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, frame.metadata.begin_frame_ack.sequence_number); - TRACE_EVENT1("cc,benchmark", "AsyncLayerTreeFrameSink::SubmitCompositorFrame", - "source_frame_number_", source_frame_number_); // It's possible to request an immediate composite from cc which will bypass // BeginFrame. In that case, we cannot collect full graphics pipeline data. @@ -202,12 +200,6 @@ } } - TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "SubmitCompositorFrame", local_surface_id_.hash()); - bool tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - &tracing_enabled); - base::Optional<viz::HitTestRegionList> hit_test_region_list; if (hit_test_data_provider_) hit_test_region_list = hit_test_data_provider_->GetHitTestData(frame); @@ -271,9 +263,7 @@ "SubmitHitTestData"); compositor_frame_sink_ptr_->SubmitCompositorFrame( - local_surface_id_, std::move(frame), std::move(hit_test_region_list), - tracing_enabled ? base::TimeTicks::Now().since_origin().InMicroseconds() - : 0); + local_surface_id_, std::move(frame), std::move(hit_test_region_list), 0); } void AsyncLayerTreeFrameSink::DidNotProduceFrame(
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 778220d..59db27eb 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -2275,7 +2275,6 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( FrameData* frame) { - TRACE_EVENT0("cc,benchmark", "LayerTreeHostImpl::GenerateCompositorFrame"); TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 3a3279d..01d5593 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -129,6 +129,16 @@ } } +if (enable_feed_in_chrome) { + android_resources("chrome_feed_java_resources") { + resource_dirs = [ "//chrome/android/feed/core/java/res" ] + deps = [ + ":chrome_app_java_resources", + ] + custom_package = "org.chromium.chrome.feed" + } +} + if (notouch_build) { android_resources("chrome_touchless_java_resources") { resource_dirs = [ "//chrome/android/touchless/java/res" ] @@ -446,6 +456,9 @@ deps += [ ":chrome_vr_java_resources" ] } srcjar_deps += [ ":chrome_vr_android_java_enums_srcjar" ] + if (enable_feed_in_chrome) { + deps += [ ":chrome_feed_java_resources" ] + } if (notouch_build) { deps += [ ":chrome_touchless_java_resources" ] }
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index 4ecdfa52..76671cc 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -58,6 +58,7 @@ "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java", + "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java", "javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java", "javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java",
diff --git a/chrome/android/features/ar/ar_module_tmpl.gni b/chrome/android/features/ar/ar_module_tmpl.gni deleted file mode 100644 index 36d7464a..0000000 --- a/chrome/android/features/ar/ar_module_tmpl.gni +++ /dev/null
@@ -1,25 +0,0 @@ -# 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. - -import("//chrome/android/features/ar/ar_module.gni") -import("//chrome/android/modules/chrome_feature_module_tmpl.gni") - -# TODO(crbug.com/968522): Remove file once downstream stops using template. -template("ar_module_tmpl") { - chrome_feature_module(target_name) { - forward_variables_from(invoker, - "*", - [ - "module_name", - "native_switches", - ]) - module_desc = ar_module_desc - uncompress_shared_libraries = true - min_sdk_version = 24 - module_name_suffix = invoker.module_name - _native_switches = invoker.native_switches - is_64_bit_browser = _native_switches.is_64_bit_browser - include_32_bit_webview = _native_switches.include_32_bit_webview - } -}
diff --git a/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni b/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni deleted file mode 100644 index 2792fa8..0000000 --- a/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni +++ /dev/null
@@ -1,17 +0,0 @@ -# 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. - -import( - "//chrome/android/features/autofill_assistant/autofill_assistant_module.gni") -import("//chrome/android/modules/chrome_feature_module_tmpl.gni") - -# TODO(crbug.com/968522): Remove file once downstream stops using template. -template("autofill_assistant_module_tmpl") { - chrome_feature_module(target_name) { - forward_variables_from(invoker, "*", [ "module_name" ]) - module_desc = autofill_assistant_module_desc - min_sdk_version = 21 - module_name_suffix = invoker.module_name - } -}
diff --git a/chrome/android/features/dev_ui/dev_ui_module_tmpl.gni b/chrome/android/features/dev_ui/dev_ui_module_tmpl.gni deleted file mode 100644 index d7b944f..0000000 --- a/chrome/android/features/dev_ui/dev_ui_module_tmpl.gni +++ /dev/null
@@ -1,16 +0,0 @@ -# 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. - -import("//chrome/android/features/dev_ui/dev_ui_module.gni") -import("//chrome/android/modules/chrome_feature_module_tmpl.gni") - -# TODO(crbug.com/968522): Remove file once downstream stops using template. -template("dev_ui_module_tmpl") { - chrome_feature_module(target_name) { - forward_variables_from(invoker, "*", [ "module_name" ]) - module_desc = dev_ui_module_desc - min_sdk_version = 24 - module_name_suffix = invoker.module_name - } -}
diff --git a/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni b/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni deleted file mode 100644 index e7ababd11..0000000 --- a/chrome/android/features/tab_ui/tab_ui_module_tmpl.gni +++ /dev/null
@@ -1,15 +0,0 @@ -# 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. - -import("//chrome/android/modules/chrome_feature_module_tmpl.gni") - -# TODO(crbug.com/968522): Remove file once downstream stops using template. -template("tab_ui_module_tmpl") { - chrome_feature_module(target_name) { - forward_variables_from(invoker, "*", [ "module_name" ]) - module_desc = tab_ui_module_desc - min_sdk_version = 21 - module_name_suffix = invoker.module_name - } -}
diff --git a/chrome/android/features/vr/vr_module_tmpl.gni b/chrome/android/features/vr/vr_module_tmpl.gni deleted file mode 100644 index 390177ef..0000000 --- a/chrome/android/features/vr/vr_module_tmpl.gni +++ /dev/null
@@ -1,27 +0,0 @@ -# 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. - -import("//chrome/android/modules/chrome_feature_module_tmpl.gni") -import("//chrome/android/modules/chrome_feature_modules.gni") -import("//device/vr/buildflags/buildflags.gni") - -assert(enable_vr) - -# TODO(crbug.com/968522): Remove file once downstream stops using template. -template("vr_module_tmpl") { - chrome_feature_module(target_name) { - forward_variables_from(invoker, - "*", - [ - "module_name", - "native_switches", - ]) - module_desc = vr_module_desc - min_sdk_version = 21 - module_name_suffix = invoker.module_name - _native_switches = invoker.native_switches - is_64_bit_browser = _native_switches.is_64_bit_browser - include_32_bit_webview = _native_switches.include_32_bit_webview - } -}
diff --git a/chrome/android/java/res/drawable-hdpi/ic_amp_24dp.png b/chrome/android/feed/core/java/res/drawable-hdpi/ic_amp_24dp.png similarity index 100% rename from chrome/android/java/res/drawable-hdpi/ic_amp_24dp.png rename to chrome/android/feed/core/java/res/drawable-hdpi/ic_amp_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_amp_24dp.png b/chrome/android/feed/core/java/res/drawable-mdpi/ic_amp_24dp.png similarity index 100% rename from chrome/android/java/res/drawable-mdpi/ic_amp_24dp.png rename to chrome/android/feed/core/java/res/drawable-mdpi/ic_amp_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_amp_24dp.png b/chrome/android/feed/core/java/res/drawable-xhdpi/ic_amp_24dp.png similarity index 100% rename from chrome/android/java/res/drawable-xhdpi/ic_amp_24dp.png rename to chrome/android/feed/core/java/res/drawable-xhdpi/ic_amp_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_amp_24dp.png b/chrome/android/feed/core/java/res/drawable-xxhdpi/ic_amp_24dp.png similarity index 100% rename from chrome/android/java/res/drawable-xxhdpi/ic_amp_24dp.png rename to chrome/android/feed/core/java/res/drawable-xxhdpi/ic_amp_24dp.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_amp_24dp.png b/chrome/android/feed/core/java/res/drawable-xxxhdpi/ic_amp_24dp.png similarity index 100% rename from chrome/android/java/res/drawable-xxxhdpi/ic_amp_24dp.png rename to chrome/android/feed/core/java/res/drawable-xxxhdpi/ic_amp_24dp.png Binary files differ
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java index c4e84a52..b64784d 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java
@@ -79,6 +79,10 @@ /** Default value for how long spinners must be shown for. */ public static final long SPINNER_MINIMUM_SHOW_TIME_MS_DEFAULT = 500; + private static final String STORAGE_MISS_THRESHOLD = "storage_miss_threshold"; + /** Default number of items that can be missing from a call to FeedStore before failing. */ + public static final long STORAGE_MISS_THRESHOLD_DEFAULT = 4; + private static final String TRIGGER_IMMEDIATE_PAGINATION = "trigger_immediate_pagination"; /** Default value for triggering immediate pagination. */ public static final boolean TRIGGER_IMMEDIATE_PAGINATION_DEFAULT = false; @@ -215,6 +219,14 @@ (int) SPINNER_MINIMUM_SHOW_TIME_MS_DEFAULT); } + /** @return The number of items that can be missing from a call to FeedStore before failing. */ + @VisibleForTesting + static long getStorageMissThreshold() { + return (long) ChromeFeatureList.getFieldTrialParamByFeatureAsInt( + ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS, STORAGE_MISS_THRESHOLD, + (int) STORAGE_MISS_THRESHOLD_DEFAULT); + } + /** * @return Whether UI initially shows "More" button upon reaching the end of known content, * when server could potentially have more content. @@ -290,6 +302,7 @@ .put(ConfigKey.SPINNER_DELAY_MS, FeedConfiguration.getSpinnerDelayMs()) .put(ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS, FeedConfiguration.getSpinnerMinimumShowTimeMs()) + .put(ConfigKey.STORAGE_MISS_THRESHOLD, FeedConfiguration.getStorageMissThreshold()) .put(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, FeedConfiguration.getTriggerImmediatePagination()) .put(ConfigKey.UNDOABLE_ACTIONS_ENABLED,
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java index 6037ab5d..6f9c897 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java
@@ -22,11 +22,11 @@ import org.chromium.base.SysUtils; import org.chromium.base.VisibleForTesting; import org.chromium.base.task.PostTask; -import org.chromium.chrome.R; import org.chromium.chrome.browser.image_fetcher.ImageFetcher; import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig; import org.chromium.chrome.browser.image_fetcher.ImageFetcherFactory; import org.chromium.chrome.browser.suggestions.ThumbnailGradient; +import org.chromium.chrome.feed.R; import org.chromium.content_public.browser.UiThreadTaskTraits; import java.util.Iterator;
diff --git a/chrome/android/java/res/layout/experimental_toolbar_button.xml b/chrome/android/java/res/layout/experimental_toolbar_button.xml index 215e7bf..42ee8cc 100644 --- a/chrome/android/java/res/layout/experimental_toolbar_button.xml +++ b/chrome/android/java/res/layout/experimental_toolbar_button.xml
@@ -8,5 +8,5 @@ xmlns:tools="http://schemas.android.com/tools" style="@style/ToolbarButton" android:layout_gravity="top" - android:paddingStart="8dp" + android:paddingStart="12dp" tools:ignore="ContentDescription"/>
diff --git a/chrome/android/java/res/layout/explore_sites_dense_category_card_view.xml b/chrome/android/java/res/layout/explore_sites_dense_category_card_view.xml new file mode 100644 index 0000000..dedd1d0e --- /dev/null +++ b/chrome/android/java/res/layout/explore_sites_dense_category_card_view.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/explore_sites_category_padding_horizontal" + android:layout_marginEnd="@dimen/explore_sites_category_padding_horizontal" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/explore_sites_dense_category_padding_vertical_bottom" + android:paddingTop="@dimen/explore_sites_dense_category_padding_vertical_top" > + + <TextView + android:id="@+id/category_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:textAppearance="@style/TextAppearance.BlackTitle2" + android:minHeight="@dimen/explore_sites_dense_category_title_height" + tools:text="Category" /> + + <org.chromium.chrome.browser.suggestions.tile.TileGridLayout + android:id="@+id/category_sites" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:orientation="horizontal" + android:columnCount="5" + android:columnOrderPreserved="true" + android:paddingTop="@dimen/explore_sites_dense_category_title_spacing"/> + +</org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView>
diff --git a/chrome/android/java/res/layout/explore_sites_dense_tile_right_view.xml b/chrome/android/java/res/layout/explore_sites_dense_tile_right_view.xml index b168293..9f2957d6 100644 --- a/chrome/android/java/res/layout/explore_sites_dense_tile_right_view.xml +++ b/chrome/android/java/res/layout/explore_sites_dense_tile_right_view.xml
@@ -9,7 +9,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="@dimen/explore_sites_dense_title_right_tile_view_width" android:layout_height="@dimen/explore_sites_dense_title_right_tile_view_height" - android:orientation="horizontal"> + android:orientation="horizontal" + app:iconCornerRadius="@dimen/explore_sites_dense_icon_corner_radius"> <!-- The icon background. --> <View android:id="@+id/tile_view_icon_background"
diff --git a/chrome/android/java/res/layout/toolbar_phone.xml b/chrome/android/java/res/layout/toolbar_phone.xml index 62a3a3f3..fe66549 100644 --- a/chrome/android/java/res/layout/toolbar_phone.xml +++ b/chrome/android/java/res/layout/toolbar_phone.xml
@@ -38,7 +38,7 @@ android:inflatedId="@+id/experimental_toolbar_button" android:layout="@layout/experimental_toolbar_button" style="@style/ToolbarButton" - android:paddingStart="8dp" + android:paddingStart="12dp" android:visibility="gone" /> <org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 24ad182..5a9c81a 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -385,13 +385,18 @@ <dimen name="explore_sites_loading_spinny_size">36dp</dimen> <dimen name="explore_sites_loading_spinny_padding">24dp</dimen> - <dimen name="explore_sites_dense_icon_corner_radius">4dp</dimen> + <dimen name="explore_sites_dense_category_title_height">20dp</dimen> + <dimen name="explore_sites_dense_category_title_spacing">8dp</dimen> + <dimen name="explore_sites_dense_category_padding_vertical_bottom">12dp</dimen> + <dimen name="explore_sites_dense_category_padding_vertical_top">8dp</dimen> + + <dimen name="explore_sites_dense_icon_corner_radius">2dp</dimen> <dimen name="explore_sites_dense_icon_size">32dp</dimen> <dimen name="explore_sites_dense_icon_text_size">16dp</dimen> <dimen name="explore_sites_dense_offline_badge_margin_start">26dp</dimen> <dimen name="explore_sites_dense_title_right_icon_margin_start">4dp</dimen> - <dimen name="explore_sites_dense_title_right_tile_view_width">104dp</dimen> + <dimen name="explore_sites_dense_title_right_tile_view_width">108dp</dimen> <dimen name="explore_sites_dense_title_right_tile_view_height">40dp</dimen> <dimen name="explore_sites_dense_title_right_title_height">32dp</dimen> <dimen name="explore_sites_dense_title_right_title_margin_start">40dp</dimen> @@ -399,7 +404,7 @@ <dimen name="explore_sites_dense_title_bottom_icon_margin_top">4dp</dimen> <dimen name="explore_sites_dense_title_bottom_tile_view_height">72dp</dimen> - <dimen name="explore_sites_dense_title_bottom_tile_view_width">64dp</dimen> + <dimen name="explore_sites_dense_title_bottom_tile_view_width">68dp</dimen> <dimen name="explore_sites_dense_title_bottom_title_margin_start">40dp</dimen> <!-- Recent tabs page -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java index c96fc0a..0dadf40 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -9,6 +9,7 @@ import android.util.AttributeSet; import android.widget.ImageView; +import org.chromium.base.VisibleForTesting; import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeFeatureList; @@ -247,4 +248,9 @@ void setDragHandleOnTouchListener(OnTouchListener l) { mDragHandle.setOnTouchListener(l); } + + @VisibleForTesting + String getTitle() { + return String.valueOf(mTitleView.getText()); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java index f0163a9..41428d84 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
@@ -445,13 +445,15 @@ } @Override - protected boolean isActivelyDraggable(ViewHolder viewHolder) { + @VisibleForTesting + public boolean isActivelyDraggable(ViewHolder viewHolder) { return isPassivelyDraggable(viewHolder) && ((BookmarkRow) viewHolder.itemView).isItemSelected(); } @Override - protected boolean isPassivelyDraggable(ViewHolder viewHolder) { + @VisibleForTesting + public boolean isPassivelyDraggable(ViewHolder viewHolder) { BookmarkItem bItem = getItemByHolder(viewHolder); return isOrderable(bItem); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleBottom.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleBottom.java index 5784e19..f405b35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleBottom.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleBottom.java
@@ -4,12 +4,17 @@ package org.chromium.chrome.browser.explore_sites; +import org.chromium.chrome.R; + /** CategoryCardViewHolderFactory for Dense Title Bottom variation. */ public class CategoryCardViewHolderFactoryDenseTitleBottom extends CategoryCardViewHolderFactory { @Override protected int getTileViewResource() { - return org.chromium.chrome.R.layout.explore_sites_dense_tile_bottom_view; + return R.layout.explore_sites_dense_tile_bottom_view; } - // TODO(angelii): Add overrides for getCategoryCardViewResource when implemented + @Override + protected int getCategoryCardViewResource() { + return R.layout.explore_sites_dense_category_card_view; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleRight.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleRight.java index 58c26278..c5c8c3cd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleRight.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactoryDenseTitleRight.java
@@ -4,12 +4,17 @@ package org.chromium.chrome.browser.explore_sites; +import org.chromium.chrome.R; + /** CategoryCardViewHolderFactory for Dense Title Right variation. */ public class CategoryCardViewHolderFactoryDenseTitleRight extends CategoryCardViewHolderFactory { @Override protected int getTileViewResource() { - return org.chromium.chrome.R.layout.explore_sites_dense_tile_right_view; + return R.layout.explore_sites_dense_tile_right_view; } - // TODO(angelii): Add overrides for getCategoryCardViewResource when implemented + @Override + protected int getCategoryCardViewResource() { + return R.layout.explore_sites_dense_category_card_view; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java index 2924bcc..73a1abb9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java
@@ -48,6 +48,8 @@ "org.chromium.chrome.browser.notifications.scheduler.EXTRA_GUID"; private static final String EXTRA_ACTION_BUTTON_TYPE = "org.chromium.chrome.browser.notifications.scheduler.EXTRA_ACTION_BUTTON_TYPE"; + private static final String EXTRA_SCHEDULER_CLIENT_TYPE = + "org.chromium.chrome.browser.notifications.scheduler.EXTRA_SCHEDULER_CLIENT_TYPE "; /** * Contains button info on the notification. @@ -68,15 +70,13 @@ * Contains all data needed to build Android notification in the UI, specified by the client. */ private static class NotificationData { - public final String id; public final String title; public final String message; public final Bitmap icon; public ArrayList<Button> buttons; - private NotificationData(String id, String title, String message, Bitmap icon) { - // TODO(xingliu): Populate custom data and client defined id. - this.id = id; + private NotificationData(String title, String message, Bitmap icon) { + // TODO(xingliu): Populate custom data. this.title = title; this.message = message; this.icon = icon; @@ -90,8 +90,8 @@ @CalledByNative private static NotificationData buildNotificationData( - String id, String title, String message, Bitmap icon) { - return new NotificationData(id, title, message, icon); + String title, String message, Bitmap icon) { + return new NotificationData(title, message, icon); } /** @@ -99,16 +99,18 @@ * notification. */ private static class SystemData { + public @SchedulerClientType int type; public final String guid; - public SystemData(String guid) { + public SystemData(@SchedulerClientType int type, String guid) { + this.type = type; this.guid = guid; } } @CalledByNative - private static SystemData buildSystemData(String guid) { - return new SystemData(guid); + private static SystemData buildSystemData(@SchedulerClientType int type, String guid) { + return new SystemData(type, guid); } /** @@ -140,19 +142,23 @@ int intentType = IntentUtils.safeGetIntExtra( intent, EXTRA_INTENT_TYPE, NotificationIntentInterceptor.IntentType.UNKNOWN); String guid = IntentUtils.safeGetStringExtra(intent, EXTRA_GUID); + @SchedulerClientType + int clientType = IntentUtils.safeGetIntExtra( + intent, EXTRA_SCHEDULER_CLIENT_TYPE, SchedulerClientType.UNKNOWN); switch (intentType) { case NotificationIntentInterceptor.IntentType.UNKNOWN: break; case NotificationIntentInterceptor.IntentType.CONTENT_INTENT: - nativeOnContentClick(Profile.getLastUsedProfile(), guid); + nativeOnContentClick(Profile.getLastUsedProfile(), clientType, guid); break; case NotificationIntentInterceptor.IntentType.DELETE_INTENT: - nativeOnDismiss(Profile.getLastUsedProfile(), guid); + nativeOnDismiss(Profile.getLastUsedProfile(), clientType, guid); break; case NotificationIntentInterceptor.IntentType.ACTION_INTENT: int actionButtonType = IntentUtils.safeGetIntExtra( intent, EXTRA_ACTION_BUTTON_TYPE, ActionButtonType.UNKNOWN_ACTION); - nativeOnActionButton(Profile.getLastUsedProfile(), guid, actionButtonType); + nativeOnActionButton( + Profile.getLastUsedProfile(), clientType, guid, actionButtonType); break; } } @@ -174,24 +180,25 @@ } private static Intent buildIntent(Context context, - @NotificationIntentInterceptor.IntentType int intentType, String guid) { + @NotificationIntentInterceptor.IntentType int intentType, SystemData systemData) { Intent intent = new Intent(context, DisplayAgent.Receiver.class); intent.putExtra(EXTRA_INTENT_TYPE, intentType); - intent.putExtra(EXTRA_GUID, guid); + intent.putExtra(EXTRA_SCHEDULER_CLIENT_TYPE, systemData.type); + intent.putExtra(EXTRA_GUID, systemData.guid); return intent; } @CalledByNative private static void showNotification(NotificationData notificationData, SystemData systemData) { AndroidNotificationData platformData = toAndroidNotificationData(notificationData); - // TODO(xingliu): Plumb platform specific data from native. Support single notification + // TODO(xingliu): Plumb platform specific data from native. // mode and provide correct notification id. Support buttons. Context context = ContextUtils.getApplicationContext(); ChromeNotificationBuilder builder = NotificationBuilderFactory.createChromeNotificationBuilder(true /* preferCompat */, platformData.channel, null /* remoteAppPackageName */, new NotificationMetadata(platformData.systemNotificationType, - DISPLAY_AGENT_TAG, DISPLAY_AGENT_NOTIFICATION_ID)); + DISPLAY_AGENT_TAG, systemData.guid.hashCode())); builder.setContentTitle(notificationData.title); builder.setContentText(notificationData.message); @@ -200,7 +207,7 @@ // Default content click behavior. Intent contentIntent = buildIntent( - context, NotificationIntentInterceptor.IntentType.CONTENT_INTENT, systemData.guid); + context, NotificationIntentInterceptor.IntentType.CONTENT_INTENT, systemData); builder.setContentIntent(PendingIntentProvider.getBroadcast(context, getRequestCode( NotificationIntentInterceptor.IntentType.CONTENT_INTENT, systemData.guid), @@ -208,7 +215,7 @@ // Default dismiss behavior. Intent dismissIntent = buildIntent( - context, NotificationIntentInterceptor.IntentType.DELETE_INTENT, systemData.guid); + context, NotificationIntentInterceptor.IntentType.DELETE_INTENT, systemData); builder.setDeleteIntent(PendingIntentProvider.getBroadcast(context, getRequestCode( NotificationIntentInterceptor.IntentType.DELETE_INTENT, systemData.guid), @@ -217,8 +224,8 @@ // Add the buttons. for (int i = 0; i < notificationData.buttons.size(); i++) { Button button = notificationData.buttons.get(i); - Intent actionIntent = buildIntent(context, - NotificationIntentInterceptor.IntentType.ACTION_INTENT, systemData.guid); + Intent actionIntent = buildIntent( + context, NotificationIntentInterceptor.IntentType.ACTION_INTENT, systemData); actionIntent.putExtra(EXTRA_ACTION_BUTTON_TYPE, button.type); // TODO(xingliu): Support button icon. See https://crbug.com/983354 @@ -249,8 +256,10 @@ private DisplayAgent() {} - private static native void nativeOnContentClick(Profile profile, String guid); - private static native void nativeOnDismiss(Profile profile, String guid); - private static native void nativeOnActionButton( - Profile profile, String guid, @ActionButtonType int type); + private static native void nativeOnContentClick( + Profile profile, @SchedulerClientType int type, String guid); + private static native void nativeOnDismiss( + Profile profile, @SchedulerClientType int type, String guid); + private static native void nativeOnActionButton(Profile profile, + @SchedulerClientType int clientType, String guid, @ActionButtonType int type); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java index b1375287..f0d98f62 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageArchivePublisherBridge.java
@@ -138,7 +138,7 @@ @CalledByNative @VisibleForTesting public static String publishArchiveToDownloadsCollection(OfflinePageItem page) { - assert !org.chromium.base.BuildInfo.isAtLeastQ(); + assert org.chromium.base.BuildInfo.isAtLeastQ(); final String isPending = "is_pending"; // MediaStore.IS_PENDING
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index 7f307c0..6b0082f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -2587,7 +2587,7 @@ */ private boolean isMenuButtonPresent() { final ImageButton menuButton = getMenuButton(); - if (menuButton != null) return false; + if (menuButton == null) return false; return menuButton.isShown(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java index b2cf96fea..45c08570 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/dragreorder/DragReorderableListAdapter.java
@@ -14,6 +14,8 @@ import android.support.v7.widget.helper.ItemTouchHelper; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.Log; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import java.util.Collections; @@ -26,11 +28,13 @@ */ public abstract class DragReorderableListAdapter<T> extends RecyclerView.Adapter<ViewHolder> { private static final int ANIMATION_DELAY_MS = 100; + private static final String TAG = "DragAdapter"; protected final Context mContext; // keep track of the list and list managers protected ItemTouchHelper mItemTouchHelper; + private ItemTouchHelper.Callback mTouchHelperCallback; protected List<T> mElements; protected RecyclerView mRecyclerView; @@ -154,8 +158,8 @@ */ public void enableDrag() { if (mItemTouchHelper == null) { - ItemTouchHelper.Callback touchHelperCallBack = new DragTouchCallback(); - mItemTouchHelper = new ItemTouchHelper(touchHelperCallBack); + mTouchHelperCallback = new DragTouchCallback(); + mItemTouchHelper = new ItemTouchHelper(mTouchHelperCallback); } mItemTouchHelper.attachToRecyclerView(mRecyclerView); } @@ -219,4 +223,27 @@ protected void setDragStateDelegate(DragStateDelegate delegate) { mDragStateDelegate = delegate; } + + /** + * Simulate a drag. All items that are involved in the drag must be visible (no scrolling). + * + * @param start The index of the ViewHolder that you want to drag. + * @param end The index this ViewHolder should be dragged to and dropped at. + */ + @VisibleForTesting + public void simulateDragForTests(int start, int end) { + ViewHolder viewHolder = mRecyclerView.findViewHolderForAdapterPosition(start); + mItemTouchHelper.startDrag(viewHolder); + int increment = start < end ? 1 : -1; + int i = start; + while (i != end) { + Log.i(TAG, "Moving ViewHolder %s from %d to %d", viewHolder, i, i + increment); + i += increment; + mTouchHelperCallback.onMove( + mRecyclerView, viewHolder, mRecyclerView.findViewHolderForAdapterPosition(i)); + } + Log.i(TAG, "Letting go of ViewHolder %s", viewHolder); + mTouchHelperCallback.onSelectedChanged(viewHolder, ItemTouchHelper.ACTION_STATE_IDLE); + mTouchHelperCallback.clearView(mRecyclerView, viewHolder); + } } \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java index d8ebef6..93f6b52e6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
@@ -18,10 +18,12 @@ import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.RetryOnFailure; +import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.util.BookmarkTestUtil; +import org.chromium.chrome.test.util.browser.Features; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.content_public.browser.test.util.TestThreadUtils; @@ -226,4 +228,42 @@ folderList.clear(); depthList.clear(); } + + @Test + @SmallTest + @UiThreadTest + @Feature({"Bookmark"}) + @Features.EnableFeatures(ChromeFeatureList.REORDER_BOOKMARKS) + public void testReorderBookmarks() { + mBookmarkBridge.addFolder(mMobileNode, 0, "a"); // ID 5 + mBookmarkBridge.addFolder(mMobileNode, 0, "b"); // ID 6 + mBookmarkBridge.addBookmark(mMobileNode, 0, "a", "http://a.com"); // ID 7 + mBookmarkBridge.addBookmark(mMobileNode, 0, "b", "http://b.com"); // ID 8 + + long[] startingIdsArray = new long[] {8, 7, 6, 5, 0}; + Assert.assertArrayEquals( + startingIdsArray, getIdArray(mBookmarkBridge.getChildIDs(mMobileNode, true, true))); + + long[] reorderedIdsArray = new long[] {7, 6, 8, 5}; + mBookmarkBridge.reorderBookmarks(mMobileNode, reorderedIdsArray); + + long[] endingIdsArray = new long[] {7, 6, 8, 5, 0}; + Assert.assertArrayEquals( + endingIdsArray, getIdArray(mBookmarkBridge.getChildIDs(mMobileNode, true, true))); + } + + /** + * Given a list of BookmarkIds, returns an array full of the (long) ID numbers. + * + * @param bIds The BookmarkIds of interest. + * @return An array containing the (long) ID numbers. + */ + private long[] getIdArray(List<BookmarkId> bIds) { + // Get the new order for the IDs. + long[] newOrder = new long[bIds.size()]; + for (int i = 0; i <= bIds.size() - 1; i++) { + newOrder[i] = bIds.get(i).getId(); + } + return newOrder; + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java new file mode 100644 index 0000000..5439edf --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
@@ -0,0 +1,615 @@ +// 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. + +package org.chromium.chrome.browser.bookmarks; + +import android.support.test.filters.MediumTest; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.View; + +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.night_mode.NightModeTestUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.params.ParameterAnnotations; +import org.chromium.base.test.params.ParameterizedRunner; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.RetryOnFailure; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; +import org.chromium.chrome.test.util.BookmarkTestUtil; +import org.chromium.chrome.test.util.browser.Features; +import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils; +import org.chromium.components.bookmarks.BookmarkId; +import org.chromium.content_public.browser.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.browser.test.util.TouchCommon; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +/** + * Tests for the bookmark manager. + */ +// clang-format off +@RunWith(ParameterizedRunner.class) +@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@Features.EnableFeatures(ChromeFeatureList.REORDER_BOOKMARKS) +@RetryOnFailure +public class BookmarkReorderTest extends BookmarkTest { + // clang-format on + + // This class extends BookmarkTest because some new features are being added, but all of the + // old functionality should remain; thus, we want to also run all of the old tests. + // This seemed the most elegant and efficient way to accomplish this. + // TODO(crbug.com/160194): Clean up after bookmark reordering launches. + + private static final String TEST_TITLE_A = "a"; + private static final String TEST_URL_A = "http://a.com"; + private static final String TAG = "BookmarkReorderTest"; + + + @Override + @Test + @MediumTest + @Feature({"RenderTest"}) + @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class) + public void testBookmarkFolderIcon(boolean nightModeEnabled) throws Exception { + Assert.assertTrue("Expected Bookmark Reordering to be enabled", ChromeFeatureList.isEnabled(ChromeFeatureList.REORDER_BOOKMARKS)); + super.testBookmarkFolderIcon(nightModeEnabled); + } + + @Test + @MediumTest + public void testEndIconVisibilityInSelectionMode() throws Exception { + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + addBookmark(TEST_TITLE_A, TEST_URL_A); + + openBookmarkManager(); + + BookmarkRow test = + (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(2).itemView; + View testMoreButton = test.findViewById(R.id.more); + View testDragHandle = test.findViewById(R.id.drag_handle); + + View testFolderA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + View aMoreButton = testFolderA.findViewById(R.id.more); + View aDragHandle = testFolderA.findViewById(R.id.drag_handle); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Callback occurs when Item "test" is selected. + CriteriaHelper.pollUiThread(test::isChecked, "Expected item \"test\" to become selected"); + + Assert.assertEquals("Expected more button of selected item to be gone when drag is active.", + View.GONE, testMoreButton.getVisibility()); + Assert.assertEquals( + "Expected drag handle of selected item to be visible when drag is active.", + View.VISIBLE, testDragHandle.getVisibility()); + Assert.assertTrue("Expected drag handle to be enabled when drag is active.", + testDragHandle.isEnabled()); + + Assert.assertEquals( + "Expected more button of unselected item to be gone when drag is active.", + View.GONE, aMoreButton.getVisibility()); + Assert.assertEquals( + "Expected drag handle of unselected item to be visible when drag is active.", + View.VISIBLE, aDragHandle.getVisibility()); + Assert.assertFalse( + "Expected drag handle of unselected item to be disabled when drag is active.", + aDragHandle.isEnabled()); + } + + @Test + @MediumTest + public void testEndIconVisiblityInSearchMode() throws Exception { + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + addFolder(TEST_TITLE_A); + + openBookmarkManager(); + + View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id); + + BookmarkRow test = + (BookmarkRow) mItemsContainer.findViewHolderForAdapterPosition(2).itemView; + View testMoreButton = test.findViewById(R.id.more); + View testDragHandle = test.findViewById(R.id.drag_handle); + + View a = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + View aMoreButton = a.findViewById(R.id.more); + View aDragHandle = a.findViewById(R.id.drag_handle); + + TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick); + + // Callback occurs when Item "test" is selected. + CriteriaHelper.pollUiThread(() + -> mBookmarkActivity.getManagerForTesting() + .getToolbarForTests() + .isSearching(), + "Expected to enter search mode"); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Callback occurs when Item "test" is selected. + CriteriaHelper.pollUiThread(test::isChecked, "Expected item \"test\" to become selected"); + + Assert.assertEquals("Expected drag handle of selected item to be gone " + + "when selection mode is activated from search.", + View.GONE, testDragHandle.getVisibility()); + Assert.assertEquals("Expected more button of selected item to be visible " + + "when selection mode is activated from search.", + View.VISIBLE, testMoreButton.getVisibility()); + Assert.assertFalse("Expected more button of selected item to be disabled " + + "when selection mode is activated from search.", + testMoreButton.isEnabled()); + + Assert.assertEquals("Expected drag handle of unselected item to be gone " + + "when selection mode is activated from search.", + View.GONE, aDragHandle.getVisibility()); + Assert.assertEquals("Expected more button of unselected item to be visible " + + "when selection mode is activated from search.", + View.VISIBLE, aMoreButton.getVisibility()); + Assert.assertFalse("Expected more button of unselected item to be disabled " + + "when selection mode is activated from search.", + aMoreButton.isEnabled()); + } + + @Test + @MediumTest + public void testSmallDrag_Up_BookmarksOnly() throws Exception { + List<BookmarkId> initial = new ArrayList<>(); + List<BookmarkId> expected = new ArrayList<>(); + BookmarkId fooId = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo); + BookmarkId googleId = addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage); + BookmarkId aId = addBookmark(TEST_TITLE_A, TEST_URL_A); + + // When bookmarks are added, they are added to the top of the list. + // The current bookmark order is the reverse of the order in which they were added. + initial.add(aId); + initial.add(googleId); + initial.add(fooId); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertEquals("Bookmarks were not added in the expected order.", initial, + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true) + .subList(0, 3)); + }); + + expected.add(fooId); + expected.add(aId); + expected.add(googleId); + + openBookmarkManager(); + + // Callback occurs upon changes inside of the bookmark model. + CallbackHelper modelReorderHelper = new CallbackHelper(); + BookmarkBridge.BookmarkModelObserver bookmarkModelObserver = + new BookmarkBridge.BookmarkModelObserver() { + @Override + public void bookmarkModelChanged() { + modelReorderHelper.notifyCalled(); + } + }; + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBookmarkModel.addObserver(bookmarkModelObserver); + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + View foo = mItemsContainer.findViewHolderForAdapterPosition(3).itemView; + Assert.assertEquals("Wrong bookmark item selected.", TEST_PAGE_TITLE_FOO, + ((BookmarkItemRow) foo).getTitle()); + View dragHandle = foo.findViewById(R.id.drag_handle); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(fooId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + // Starts as last bookmark (2nd index) and ends as 0th bookmark (promo header not included). + TestThreadUtils.runOnUiThreadBlocking(() -> { + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()).simulateDragForTests(3, 1); + }); + + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + List<BookmarkId> observed = + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true); + // Exclude partner bookmarks folder + Assert.assertEquals(expected, observed.subList(0, 3)); + }); + } + + @Test + @MediumTest + public void testSmallDrag_Down_FoldersOnly() throws Exception { + List<BookmarkId> initial = new ArrayList<>(); + List<BookmarkId> expected = new ArrayList<>(); + BookmarkId aId = addFolder("a"); + BookmarkId bId = addFolder("b"); + BookmarkId cId = addFolder("c"); + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + + initial.add(testId); + initial.add(cId); + initial.add(bId); + initial.add(aId); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertEquals("Bookmarks were not added in the expected order.", initial, + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true) + .subList(0, 4)); + }); + + expected.add(cId); + expected.add(bId); + expected.add(aId); + expected.add(testId); + + openBookmarkManager(); + + // Callback occurs upon changes inside of the bookmark model. + CallbackHelper modelReorderHelper = new CallbackHelper(); + BookmarkBridge.BookmarkModelObserver bookmarkModelObserver = + new BookmarkBridge.BookmarkModelObserver() { + @Override + public void bookmarkModelChanged() { + modelReorderHelper.notifyCalled(); + } + }; + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBookmarkModel.addObserver(bookmarkModelObserver); + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) test).getTitle()); + View dragHandle = test.findViewById(R.id.drag_handle); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + // Starts as 0th bookmark (not counting promo header) and ends as last (index 3). + TestThreadUtils.runOnUiThreadBlocking(() -> { + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()).simulateDragForTests(1, 4); + }); + + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + List<BookmarkId> observed = + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true); + // Exclude partner bookmarks folder + Assert.assertEquals(expected, observed.subList(0, 4)); + }); + } + + @Test + @MediumTest + public void testSmallDrag_Down_MixedFoldersAndBookmarks() throws Exception { + List<BookmarkId> initial = new ArrayList<>(); + List<BookmarkId> expected = new ArrayList<>(); + BookmarkId aId = addFolder("a"); + BookmarkId bId = addBookmark("b", "http://b.com"); + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + + initial.add(testId); + initial.add(bId); + initial.add(aId); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + Assert.assertEquals("Bookmarks were not added in the expected order.", initial, + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true) + .subList(0, 3)); + }); + + expected.add(bId); + expected.add(testId); + expected.add(aId); + + openBookmarkManager(); + + // Callback occurs upon changes inside of the bookmark model. + CallbackHelper modelReorderHelper = new CallbackHelper(); + BookmarkBridge.BookmarkModelObserver bookmarkModelObserver = + new BookmarkBridge.BookmarkModelObserver() { + @Override + public void bookmarkModelChanged() { + modelReorderHelper.notifyCalled(); + } + }; + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBookmarkModel.addObserver(bookmarkModelObserver); + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + View test = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) test).getTitle()); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + // Starts as 0th bookmark (not counting promo header) and ends at the 1st index. + TestThreadUtils.runOnUiThreadBlocking(() -> { + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()).simulateDragForTests(1, 2); + }); + + modelReorderHelper.waitForCallback(0, 1); + RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + List<BookmarkId> observed = + mBookmarkModel.getChildIDs(mBookmarkModel.getDefaultFolder(), true, true); + // Exclude partner bookmarks folder + Assert.assertEquals(expected, observed.subList(0, 3)); + }); + } + + @Test + @MediumTest + public void testPromoDraggability() throws Exception { + BookmarkId testId = addFolder(TEST_FOLDER_TITLE); + + openBookmarkManager(); + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + ViewHolder promo = mItemsContainer.findViewHolderForAdapterPosition(0); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + ReorderBookmarkItemsAdapter adapter = + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()); + Assert.assertFalse("Promo header should not be passively draggable", + adapter.isPassivelyDraggable(promo)); + Assert.assertFalse("Promo header should not be actively draggable", + adapter.isActivelyDraggable(promo)); + } + + @Test + @MediumTest + public void testPartnerFolderDraggability() throws Exception { + BookmarkId testId = addFolderWithPartner(TEST_FOLDER_TITLE); + openBookmarkManager(); + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + ViewHolder partner = mItemsContainer.findViewHolderForAdapterPosition(2); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(testId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + ReorderBookmarkItemsAdapter adapter = + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()); + Assert.assertFalse("Partner bookmarks folder should not be passively draggable", + adapter.isPassivelyDraggable(partner)); + Assert.assertFalse("Partner bookmarks folder should not be actively draggable", + adapter.isActivelyDraggable(partner)); + } + + @Test + @MediumTest + public void testUnselectedItemDraggability() throws Exception { + BookmarkId aId = addBookmark("a", "http://a.com"); + addFolder(TEST_FOLDER_TITLE); + + openBookmarkManager(); + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + + // Perform registration to make all of these callbacks work. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + ViewHolder test = mItemsContainer.findViewHolderForAdapterPosition(1); + Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE, + ((BookmarkFolderRow) test.itemView).getTitle()); + + TestThreadUtils.runOnUiThreadBlocking( + () -> mManager.getSelectionDelegate().toggleSelectionForItem(aId)); + + // Entering selection mode - items are getting redrawn. + layoutHelper.waitForCallback(); + + ReorderBookmarkItemsAdapter adapter = + ((ReorderBookmarkItemsAdapter) mItemsContainer.getAdapter()); + Assert.assertTrue("Unselected rows should be passively draggable", + adapter.isPassivelyDraggable(test)); + Assert.assertFalse("Unselected rows should not be actively draggable", + adapter.isActivelyDraggable(test)); + } + + @Test + @MediumTest + public void testCannotSelectPromo() throws Exception { + addFolder(TEST_FOLDER_TITLE); + + openBookmarkManager(); + + // Callback occurs upon changes to the bookmark model. + CallbackHelper modelReorderHelper = new CallbackHelper(); + BookmarkBridge.BookmarkModelObserver bookmarkModelObserver = + new BookmarkBridge.BookmarkModelObserver() { + @Override + public void bookmarkModelChanged() { + modelReorderHelper.notifyCalled(); + } + }; + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBookmarkModel.addObserver(bookmarkModelObserver); + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + View promo = mItemsContainer.findViewHolderForAdapterPosition(0).itemView; + TouchCommon.longPressView(promo); + Assert.assertFalse("Expected that we would not be in selection mode " + + "after long pressing on promo view.", + mManager.getSelectionDelegate().isSelectionEnabled()); + } + + @Test + @MediumTest + public void testCannotSelectPartner() throws Exception { + addFolderWithPartner(TEST_FOLDER_TITLE); + openBookmarkManager(); + + // Callback occurs upon changes to the bookmark model. + CallbackHelper modelReorderHelper = new CallbackHelper(); + BookmarkBridge.BookmarkModelObserver bookmarkModelObserver = + new BookmarkBridge.BookmarkModelObserver() { + @Override + public void bookmarkModelChanged() { + modelReorderHelper.notifyCalled(); + } + }; + + // Callback occurs upon layout changes. + CallbackHelper layoutHelper = new CallbackHelper(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mBookmarkModel.addObserver(bookmarkModelObserver); + mItemsContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, + oldBottom) -> layoutHelper.notifyCalled()); + }); + + View partner = mItemsContainer.findViewHolderForAdapterPosition(2).itemView; + TouchCommon.longPressView(partner); + Assert.assertFalse("Expected that we would not be in selection mode " + + "after long pressing on partner bookmark.", + mManager.getSelectionDelegate().isSelectionEnabled()); + } + + /** + * Loads an empty partner bookmarks folder for testing. The partner bookmarks folder will appear + * in the mobile bookmarks folder. + * + * @throws InterruptedException If the loading process is interrupted. + */ + private void loadEmptyPartnerBookmarksForTesting() throws InterruptedException { + TestThreadUtils.runOnUiThreadBlocking( + () -> { mBookmarkModel.loadEmptyPartnerBookmarkShimForTesting(); }); + BookmarkTestUtil.waitForBookmarkModelLoaded(); + } + + /** + * Adds a bookmark in the scenario where we have partner bookmarks. + * + * @param title The title of the bookmark to add. + * @param url The url of the bookmark to add. + * @return The BookmarkId of the added bookmark. + * @throws InterruptedException If this operation is interrupted. + * @throws ExecutionException If something goes wrong while we are trying to add the bookmark. + */ + private BookmarkId addBookmarkWithPartner(String title, String url) + throws InterruptedException, ExecutionException { + loadEmptyPartnerBookmarksForTesting(); + return TestThreadUtils.runOnUiThreadBlocking( + () -> mBookmarkModel.addBookmark(mBookmarkModel.getDefaultFolder(), 0, title, url)); + } + + /** + * Adds a folder in the scenario where we have partner bookmarks. + * + * @param title The title of the folder to add. + * @return The BookmarkId of the added folder. + * @throws InterruptedException If this operation is interrupted. + * @throws ExecutionException If something goes wrong while we are trying to add the bookmark. + */ + private BookmarkId addFolderWithPartner(String title) + throws InterruptedException, ExecutionException { + loadEmptyPartnerBookmarksForTesting(); + return TestThreadUtils.runOnUiThreadBlocking( + () -> mBookmarkModel.addFolder(mBookmarkModel.getDefaultFolder(), 0, title)); + } + + private ReorderBookmarkItemsAdapter getReorderAdapter() { + return (ReorderBookmarkItemsAdapter) getAdapter(); + } + + @Override + protected BookmarkManager getBookmarkManager() { + return (BookmarkManager) getReorderAdapter().getDelegateForTesting(); + } + + @Override + protected BookmarkId getIdByPosition(int pos) { + return getReorderAdapter().getIdByPosition(pos); + } + + @Override + protected void searchBookmarks(final String query) { + TestThreadUtils.runOnUiThreadBlocking(() -> getReorderAdapter().search(query)); + } +} \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java index 6900b5a8..67d4805 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -37,6 +37,7 @@ import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem; import org.chromium.chrome.browser.night_mode.NightModeTestUtils; @@ -50,8 +51,10 @@ import org.chromium.chrome.test.util.ChromeTabUtils; import org.chromium.chrome.test.util.MenuUtils; import org.chromium.chrome.test.util.RenderTestRule; +import org.chromium.chrome.test.util.browser.Features; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkType; +import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TouchCommon; import org.chromium.net.test.EmbeddedTestServer; @@ -64,11 +67,14 @@ /** * Tests for the bookmark manager. */ +// clang-format off @RunWith(ParameterizedRunner.class) @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @RetryOnFailure +@Features.DisableFeatures(ChromeFeatureList.REORDER_BOOKMARKS) public class BookmarkTest { + // clang-format on @Rule public ChromeActivityTestRule<ChromeActivity> mActivityTestRule = new ChromeActivityTestRule<>(ChromeActivity.class); @@ -76,20 +82,20 @@ @Rule public RenderTestRule mRenderTestRule = new RenderTestRule(); - private static final String TEST_PAGE_URL_GOOGLE = "/chrome/test/data/android/google.html"; - private static final String TEST_PAGE_TITLE_GOOGLE = "The Google"; - private static final String TEST_PAGE_TITLE_GOOGLE2 = "Google"; - private static final String TEST_PAGE_URL_FOO = "/chrome/test/data/android/test.html"; - private static final String TEST_PAGE_TITLE_FOO = "Foo"; - private static final String TEST_FOLDER_TITLE = "Test folder"; + protected static final String TEST_PAGE_URL_GOOGLE = "/chrome/test/data/android/google.html"; + protected static final String TEST_PAGE_TITLE_GOOGLE = "The Google"; + protected static final String TEST_PAGE_TITLE_GOOGLE2 = "Google"; + protected static final String TEST_PAGE_URL_FOO = "/chrome/test/data/android/test.html"; + protected static final String TEST_PAGE_TITLE_FOO = "Foo"; + protected static final String TEST_FOLDER_TITLE = "Test folder"; - private BookmarkManager mManager; - private BookmarkModel mBookmarkModel; - private RecyclerView mItemsContainer; - private String mTestPage; - private String mTestPageFoo; - private EmbeddedTestServer mTestServer; - private @Nullable BookmarkActivity mBookmarkActivity; + protected BookmarkManager mManager; + protected BookmarkModel mBookmarkModel; + protected RecyclerView mItemsContainer; + protected String mTestPage; + protected String mTestPageFoo; + protected EmbeddedTestServer mTestServer; + protected @Nullable BookmarkActivity mBookmarkActivity; @BeforeClass public static void setUpBeforeActivityLaunched() { @@ -114,7 +120,7 @@ mTestPageFoo = mTestServer.getURL(TEST_PAGE_URL_FOO); } - private void readPartnerBookmarks() throws InterruptedException { + protected void readPartnerBookmarks() throws InterruptedException { // Do not read partner bookmarks in setUp(), so that the lazy reading is covered. TestThreadUtils.runOnUiThreadBlocking( () -> PartnerBookmarksShim.kickOffReading(mActivityTestRule.getActivity())); @@ -131,7 +137,7 @@ NightModeTestUtils.tearDownNightModeAfterChromeActivityDestroyed(); } - private void openBookmarkManager() throws InterruptedException { + protected void openBookmarkManager() throws InterruptedException { if (mActivityTestRule.getActivity().isTablet()) { mActivityTestRule.loadUrl(UrlConstants.BOOKMARKS_URL); mItemsContainer = @@ -147,6 +153,7 @@ new MenuUtils.MenuActivityTrigger(InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity(), R.id.all_bookmarks_menu_id)); mItemsContainer = (RecyclerView) mBookmarkActivity.findViewById(R.id.recycler_view); + mItemsContainer.setItemAnimator(null); // Disable animation to reduce flakiness. mManager = mBookmarkActivity.getManagerForTesting(); } } @@ -156,8 +163,8 @@ @Override public Boolean call() throws Exception { for (int i = 0; i < mItemsContainer.getAdapter().getItemCount(); i++) { - BookmarkId item = - ((BookmarkItemsAdapter) mItemsContainer.getAdapter()).getItem(i); + BookmarkId item = getIdByPosition(i); + if (item == null) continue; String actualTitle = mBookmarkModel.getBookmarkTitle(item); @@ -229,8 +236,7 @@ @FlakyTest(message = "crbug.com/879803") public void testOpenBookmarkManager() throws InterruptedException { openBookmarkManager(); - BookmarkDelegate delegate = - ((BookmarkItemsAdapter) mItemsContainer.getAdapter()).getDelegateForTesting(); + BookmarkDelegate delegate = getBookmarkManager(); Assert.assertEquals(BookmarkUIState.STATE_FOLDER, delegate.getCurrentState()); Assert.assertEquals("chrome-native://bookmarks/folder/3", @@ -243,8 +249,7 @@ public void testFolderNavigation_Phone() throws InterruptedException, ExecutionException { BookmarkId testFolder = addFolder(TEST_FOLDER_TITLE); openBookmarkManager(); - final BookmarkDelegate delegate = - ((BookmarkItemsAdapter) mItemsContainer.getAdapter()).getDelegateForTesting(); + final BookmarkDelegate delegate = getBookmarkManager(); final BookmarkActionBar toolbar = ((BookmarkManager) delegate).getToolbarForTests(); // Open the "Mobile bookmarks" folder. @@ -297,8 +302,8 @@ addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo); openBookmarkManager(); - BookmarkItemsAdapter adapter = ((BookmarkItemsAdapter) mItemsContainer.getAdapter()); - final BookmarkDelegate delegate = adapter.getDelegateForTesting(); + RecyclerView.Adapter adapter = getAdapter(); + final BookmarkDelegate delegate = getBookmarkManager(); // Open the default folder where these bookmarks were created. TestThreadUtils.runOnUiThreadBlocking( @@ -347,8 +352,8 @@ addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo); openBookmarkManager(); - BookmarkItemsAdapter adapter = ((BookmarkItemsAdapter) mItemsContainer.getAdapter()); - BookmarkManager manager = (BookmarkManager) adapter.getDelegateForTesting(); + RecyclerView.Adapter adapter = getAdapter(); + BookmarkManager manager = getBookmarkManager(); Assert.assertEquals("Wrong state, should be in folder", BookmarkUIState.STATE_FOLDER, manager.getCurrentState()); @@ -362,7 +367,7 @@ // Select the folder and delete it. TestThreadUtils.runOnUiThreadBlocking( - () -> manager.getSelectionDelegate().toggleSelectionForItem(adapter.getItem(0))); + () -> manager.getSelectionDelegate().toggleSelectionForItem(getIdByPosition(0))); TestThreadUtils.runOnUiThreadBlocking( () -> manager.getToolbarForTests().onMenuItemClick( manager.getToolbarForTests().getMenu().findItem( @@ -411,24 +416,29 @@ addFolder(TEST_FOLDER_TITLE); openBookmarkManager(); - BookmarkItemsAdapter adapter = ((BookmarkItemsAdapter) mItemsContainer.getAdapter()); - BookmarkManager manager = (BookmarkManager) adapter.getDelegateForTesting(); + RecyclerView.Adapter adapter = getAdapter(); + final BookmarkManager manager = getBookmarkManager(); mRenderTestRule.render(manager.getView(), "bookmark_manager_one_folder"); + BookmarkRow itemView = (BookmarkRow) manager.getRecyclerViewForTests() + .findViewHolderForAdapterPosition(0) + .itemView; + TestThreadUtils.runOnUiThreadBlocking(() -> { - BookmarkRow itemView = (BookmarkRow) manager.getRecyclerViewForTests() - .findViewHolderForAdapterPosition(0) - .itemView; itemView.performLongClick(); itemView.endAnimationsForTests(); manager.getToolbarForTests().endAnimationsForTesting(); }); + // Callback occurs when Item "test" is selected. + CriteriaHelper.pollUiThread( + itemView::isChecked, "Expected item \"test\" to become selected"); + mRenderTestRule.render(manager.getView(), "bookmark_manager_folder_selected"); TestThreadUtils.runOnUiThreadBlocking( - () -> manager.getSelectionDelegate().toggleSelectionForItem(adapter.getItem(0))); + () -> manager.getSelectionDelegate().toggleSelectionForItem(getIdByPosition(0))); mRenderTestRule.render(manager.getView(), "bookmark_manager_one_folder"); } @@ -476,7 +486,7 @@ * @param expectedText The expected description text. * @return The unique view, if one exists. Throws an exception if one doesn't exist. */ - private static View getViewWithText(final ViewGroup viewGroup, final String expectedText) { + protected static View getViewWithText(final ViewGroup viewGroup, final String expectedText) { return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<View>() { @Override public View call() throws Exception { @@ -496,14 +506,14 @@ }); } - private BookmarkId addBookmark(final String title, final String url) + protected BookmarkId addBookmark(final String title, final String url) throws InterruptedException, ExecutionException { readPartnerBookmarks(); return TestThreadUtils.runOnUiThreadBlocking( () -> mBookmarkModel.addBookmark(mBookmarkModel.getDefaultFolder(), 0, title, url)); } - private BookmarkId addFolder(final String title) + protected BookmarkId addFolder(final String title) throws InterruptedException, ExecutionException { readPartnerBookmarks(); return TestThreadUtils.runOnUiThreadBlocking( @@ -514,8 +524,23 @@ TestThreadUtils.runOnUiThreadBlocking(() -> mBookmarkModel.deleteBookmark(bookmarkId)); } - private void searchBookmarks(final String query) { - TestThreadUtils.runOnUiThreadBlocking( - () -> ((BookmarkItemsAdapter) mItemsContainer.getAdapter()).search(query)); + protected RecyclerView.Adapter getAdapter() { + return mItemsContainer.getAdapter(); + } + + private BookmarkItemsAdapter getBookmarkItemsAdapter() { + return (BookmarkItemsAdapter) getAdapter(); + } + + protected BookmarkManager getBookmarkManager() { + return (BookmarkManager) getBookmarkItemsAdapter().getDelegateForTesting(); + } + + protected BookmarkId getIdByPosition(int pos) { + return getBookmarkItemsAdapter().getItem(pos); + } + + protected void searchBookmarks(final String query) { + TestThreadUtils.runOnUiThreadBlocking(() -> getBookmarkItemsAdapter().search(query)); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java index 75d96c89..0e25918 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedConfigurationTest.java
@@ -62,6 +62,8 @@ FeedConfiguration.SPINNER_DELAY_MS_DEFAULT, FeedConfiguration.getSpinnerDelayMs()); Assert.assertEquals(FeedConfiguration.SPINNER_MINIMUM_SHOW_TIME_MS_DEFAULT, FeedConfiguration.getSpinnerMinimumShowTimeMs()); + Assert.assertEquals(FeedConfiguration.STORAGE_MISS_THRESHOLD_DEFAULT, + FeedConfiguration.getStorageMissThreshold()); Assert.assertEquals(FeedConfiguration.TRIGGER_IMMEDIATE_PAGINATION_DEFAULT, FeedConfiguration.getTriggerImmediatePagination()); Assert.assertEquals(FeedConfiguration.UNDOABLE_ACTIONS_ENABLED_DEFAULT, @@ -209,6 +211,16 @@ @Feature({"Feed"}) @CommandLineFlags. Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group", + "force-fieldtrial-params=Trial.Group:storage_miss_threshold/444"}) + public void + testStorageMissThreshold() { + Assert.assertEquals(444, FeedConfiguration.getStorageMissThreshold()); + } + + @Test + @Feature({"Feed"}) + @CommandLineFlags. + Add({"enable-features=InterestFeedContentSuggestions<Trial", "force-fieldtrials=Trial/Group", "force-fieldtrial-params=Trial.Group:spinner_minimum_show_time/444"}) public void testSpinnerMinimumShowTimeMs() { @@ -298,6 +310,8 @@ configuration.getValueOrDefault(ConfigKey.SPINNER_DELAY_MS, 0l)); Assert.assertEquals((long) FeedConfiguration.SPINNER_MINIMUM_SHOW_TIME_MS_DEFAULT, configuration.getValueOrDefault(ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS, 0l)); + Assert.assertEquals((long) FeedConfiguration.STORAGE_MISS_THRESHOLD_DEFAULT, + configuration.getValueOrDefault(ConfigKey.STORAGE_MISS_THRESHOLD, 0l)); Assert.assertFalse( configuration.getValueOrDefault(ConfigKey.TRIGGER_IMMEDIATE_PAGINATION, true)); Assert.assertFalse(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java index 0e9d1b7..52b35d2c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.preferences.website; +import android.app.Activity; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.v7.preference.Preference; @@ -27,6 +28,7 @@ import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ContentSettingsType; +import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.infobar.InfoBarContainer; import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreferenceCompat; import org.chromium.chrome.browser.preferences.ChromeSwitchPreferenceCompat; @@ -405,15 +407,18 @@ @Feature({"Preferences"}) public void testPopupsBlocked() throws Exception { setEnablePopups(false); - int activitiesCount = ApplicationStatus.getRunningActivities().size(); // Test that the popup doesn't open. mActivityTestRule.loadUrl(mTestServer.getURL("/chrome/test/data/android/popup.html")); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); if (FeatureUtilities.isNoTouchModeEnabled()) { - // Popups open in a new activity in touchless mode. - Assert.assertEquals(ApplicationStatus.getRunningActivities().size(), activitiesCount); + // Popups open in a CustomTabActivity in touchless mode. + for (Activity activity : ApplicationStatus.getRunningActivities()) { + Assert.assertFalse( + "Popup was not blocked, an instance of CustomTabActivity is running", + activity instanceof CustomTabActivity); + } } else { Assert.assertEquals(1, getTabCount()); } @@ -428,16 +433,17 @@ @Feature({"Preferences"}) public void testPopupsNotBlocked() throws Exception { setEnablePopups(true); - int activitiesCount = ApplicationStatus.getRunningActivities().size(); // Test that a popup opens. mActivityTestRule.loadUrl(mTestServer.getURL("/chrome/test/data/android/popup.html")); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); if (FeatureUtilities.isNoTouchModeEnabled()) { - // Popups open in a new activity in touchless mode. - Assert.assertEquals( - 1, ApplicationStatus.getRunningActivities().size() - activitiesCount); + // Popups open in a CustomTabActivity in touchless mode. + for (Activity activity : ApplicationStatus.getRunningActivities()) { + if (activity instanceof CustomTabActivity) return; + } + Assert.fail("Popup was blocked, no instance of CustomTabActivity is running"); } else { Assert.assertEquals(2, getTabCount()); }
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni index abefaaf..40c46f8d 100644 --- a/chrome/android/trichrome.gni +++ b/chrome/android/trichrome.gni
@@ -81,6 +81,10 @@ } else { product_version_resources_dep = "//chrome/android:product_version_resources" + + # TODO(crbug.com/956839): Remove this once R8 fixes the class merging + # and/or inlining bugs. + disable_r8_outlining = true } # TODO(torne): using system_webview_resources just to get a temporary icon
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1b88489..6f97479 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -9463,19 +9463,19 @@ <!-- Native File System permission dialog --> <message name="IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE" desc="Title of dialog for confirming write access to a file using the Native File System API"> - Save changes to original file? + Save changes to <ph name="FILENAME">$1<ex>README.md</ex></ph>? </message> <message name="IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE" desc="Title of dialog for confirming write access to a directory and the files it contains using the Native File System API"> - Save changes to original files? + Save changes to files? </message> <message name="IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT" desc="Text of the dialog for confirming write access to files using the Native File System API"> - <ph name="ORIGIN">$1<ex>example.com</ex></ph> will be able to save your changes directly to the following file. This site can save changes only while this tab is open. + <ph name="ORIGIN">$1<ex>example.com</ex></ph> will be able to save your changes directly to <ph name="FILENAME">$2<ex>README.md</ex></ph>. The site can save changes until you close this tab. </message> <message name="IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT" desc="Text of the dialog for confirming write access to a directory using the Native File System API"> - <ph name="ORIGIN">$1<ex>example.com</ex></ph> will be able to save your changes directly to the files in the following folder. This site can save changes only while this tab is open. + <ph name="ORIGIN">$1<ex>example.com</ex></ph> will be able to save your changes directly to the files in <ph name="FOLDERNAME">$2<ex>My Project</ex></ph>. The site can save changes until you close this tab. </message> <message name="IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT" desc="Text of the 'allow' button in the dialog for confirming write access to files or directories using the Native File System API"> - Save to original file + Save changes </message> <!-- Native File System usage indicator -->
diff --git a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT.png.sha1 index 6a7c160d..4fcded83 100644 --- a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT.png.sha1
@@ -1 +1 @@ -f88c5196f6a0b2de0569a82e7fd7cb15d05d995b \ No newline at end of file +4d38a4af9ba5cc2ceb9371cec583cdf4cba3a837 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT.png.sha1 index 8658d58..2b70ad3d 100644 --- a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT.png.sha1
@@ -1 +1 @@ -29a9ce8ff3e542bca3084cb4bb6516f4ad6c6ca1 \ No newline at end of file +8ed3a2331e2b5c6d9c1ead047e9a580eb977c822 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE.png.sha1 index 8658d58..2b70ad3d 100644 --- a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE.png.sha1
@@ -1 +1 @@ -29a9ce8ff3e542bca3084cb4bb6516f4ad6c6ca1 \ No newline at end of file +8ed3a2331e2b5c6d9c1ead047e9a580eb977c822 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT.png.sha1 index 6a7c160d..4fcded83 100644 --- a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT.png.sha1
@@ -1 +1 @@ -f88c5196f6a0b2de0569a82e7fd7cb15d05d995b \ No newline at end of file +4d38a4af9ba5cc2ceb9371cec583cdf4cba3a837 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE.png.sha1 index 6a7c160d..4fcded83 100644 --- a/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE.png.sha1
@@ -1 +1 @@ -f88c5196f6a0b2de0569a82e7fd7cb15d05d995b \ No newline at end of file +4d38a4af9ba5cc2ceb9371cec583cdf4cba3a837 \ No newline at end of file
diff --git a/chrome/app/helper-gpu-entitlements.plist b/chrome/app/helper-gpu-entitlements.plist new file mode 100644 index 0000000..a1c430a --- /dev/null +++ b/chrome/app/helper-gpu-entitlements.plist
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.cs.allow-unsigned-executable-memory</key> + <true/> +</dict> +</plist>
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc index cc235a3..27932643 100644 --- a/chrome/browser/android/bookmarks/bookmark_bridge.cc +++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -157,6 +157,7 @@ return; partner_bookmarks_shim_->SetPartnerBookmarksRoot( std::make_unique<BookmarkPermanentNode>(0, BookmarkNode::FOLDER)); + PartnerBookmarksShim::DisablePartnerBookmarksEditing(); DCHECK(partner_bookmarks_shim_->IsLoaded()); }
diff --git a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc index 35843fa..6f183af3 100644 --- a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc +++ b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.cc
@@ -84,6 +84,11 @@ view_->Show(); } +base::Optional<int32_t> AutofillKeyboardAccessoryAdapter::GetAxUniqueId() { + NOTIMPLEMENTED() << "See https://crbug.com/985927"; + return base::nullopt; +} + // AutofillPopupController implementation. void AutofillKeyboardAccessoryAdapter::AcceptSuggestion(int index) {
diff --git a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h index c8b597d4..b0a84ea 100644 --- a/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h +++ b/chrome/browser/autofill/autofill_keyboard_accessory_adapter.h
@@ -61,6 +61,7 @@ void OnSelectedRowChanged(base::Optional<int> previous_row_selection, base::Optional<int> current_row_selection) override; void OnSuggestionsChanged() override; + base::Optional<int32_t> GetAxUniqueId() override; // AutofillPopupController implementation. // Hidden: void OnSuggestionsChanged() override;
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc index eb77b42..61547350 100644 --- a/chrome/browser/captive_portal/captive_portal_browsertest.cc +++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -2670,9 +2670,8 @@ // The second check finds no captive portal. The reloader triggers a reload at // the same time SSL error handler tries to show an interstitial. Should result // in an SSL interstitial. -// TODO(crbug.com/981992): Reenable this after de-flaking it. IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, - DISABLED_InterstitialTimerCertErrorAfterSlowLoad) { + InterstitialTimerCertErrorAfterSlowLoad) { net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); GURL cert_error_url;
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 4b0eba4..5125ede2 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc
@@ -1484,12 +1484,7 @@ case TryChromeDialog::NOT_NOW: return chrome::RESULT_CODE_NORMAL_EXIT_CANCEL; case TryChromeDialog::OPEN_CHROME_WELCOME: - browser_creator_->set_welcome_back_page( - StartupBrowserCreator::WelcomeBackPage::kWelcomeStandard); - break; - case TryChromeDialog::OPEN_CHROME_WELCOME_WIN10: - browser_creator_->set_welcome_back_page( - StartupBrowserCreator::WelcomeBackPage::kWelcomeWin10); + browser_creator_->set_welcome_back_page(true); break; case TryChromeDialog::OPEN_CHROME_DEFAULT: break;
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc index feeb0134..b435d54d 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -672,14 +672,6 @@ const bool enabled = profile_->GetPrefs()->GetBoolean( ash::prefs::kAccessibilityAutoclickEnabled); - // The Autoclick extension work is behind a flag. Don't load the extension - // if the flag is not set. - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch( - ::switches::kEnableExperimentalAccessibilityAutoclick)) { - return; - } - if (enabled) autoclick_extension_loader_->SetProfile( profile_, base::Closure() /* done_callback */);
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc index 18ebbd4..ea89dbe 100644 --- a/chrome/browser/chromeos/arc/arc_service_launcher.cc +++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -61,6 +61,7 @@ #include "components/arc/disk_quota/arc_disk_quota_bridge.h" #include "components/arc/ime/arc_ime_service.h" #include "components/arc/intent_helper/arc_intent_helper_bridge.h" +#include "components/arc/keymaster/arc_keymaster_bridge.h" #include "components/arc/lock_screen/arc_lock_screen_bridge.h" #include "components/arc/media_session/arc_media_session_bridge.h" #include "components/arc/metrics/arc_metrics_service.h" @@ -184,6 +185,7 @@ ArcImeService::GetForBrowserContext(profile); ArcInputMethodManagerService::GetForBrowserContext(profile); ArcIntentHelperBridge::GetForBrowserContext(profile); + ArcKeymasterBridge::GetForBrowserContext(profile); ArcKioskBridge::GetForBrowserContext(profile); ArcLockScreenBridge::GetForBrowserContext(profile); ArcMediaSessionBridge::GetForBrowserContext(profile);
diff --git a/chrome/browser/chromeos/extensions/accessibility_features_apitest.cc b/chrome/browser/chromeos/extensions/accessibility_features_apitest.cc index b386583a..ea9ba5f 100644 --- a/chrome/browser/chromeos/extensions/accessibility_features_apitest.cc +++ b/chrome/browser/chromeos/extensions/accessibility_features_apitest.cc
@@ -236,7 +236,6 @@ // break this assumption as it would induce loading of ChromeVox extension. std::vector<std::string> enabled_features; enabled_features.push_back("stickyKeys"); - enabled_features.push_back("autoclick"); enabled_features.push_back("virtualKeyboard"); std::vector<std::string> disabled_features; @@ -244,6 +243,7 @@ disabled_features.push_back("largeCursor"); disabled_features.push_back("highContrast"); disabled_features.push_back("screenMagnifier"); + disabled_features.push_back("autoclick"); ASSERT_TRUE( InitPrefServiceForTest(GetPrefs(), enabled_features, disabled_features));
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc index 2f63eff..95152d04 100644 --- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc +++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -15,7 +15,6 @@ #include "base/command_line.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" -#include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" @@ -101,7 +100,6 @@ FontSettingsEventRouter::FontSettingsEventRouter(Profile* profile) : profile_(profile) { TRACE_EVENT0("browser,startup", "FontSettingsEventRouter::ctor") - SCOPED_UMA_HISTOGRAM_TIMER("Extensions.FontSettingsEventRouterCtorTime"); registrar_.Init(profile_->GetPrefs());
diff --git a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc index 0198b9ed..ef5dbf77c 100644 --- a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc +++ b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
@@ -49,8 +49,10 @@ } }; +// Test has been flaky on all platforms. https://crbug.com/985936. // Tests chrome.management.uninstallSelf API. -IN_PROC_BROWSER_TEST_P(ManagementApiNonPersistentApiTest, UninstallSelf) { +IN_PROC_BROWSER_TEST_P(ManagementApiNonPersistentApiTest, + DISABLED_UninstallSelf) { constexpr char kEventPageBackgroundScript[] = R"({"scripts": ["script.js"]})"; constexpr char kServiceWorkerBackgroundScript[] = R"({"service_worker": "script.js"})";
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc index 42c6659a..0c05134 100644 --- a/chrome/browser/extensions/extension_management.cc +++ b/chrome/browser/extensions/extension_management.cc
@@ -314,7 +314,6 @@ void ExtensionManagement::Refresh() { TRACE_EVENT0("browser,startup", "ExtensionManagement::Refresh"); - SCOPED_UMA_HISTOGRAM_TIMER("Extensions.ExtensionManagement_RefreshTime"); // Load all extension management settings preferences. const base::ListValue* allowed_list_pref = static_cast<const base::ListValue*>(LoadPreference(
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn index 2d3ee40..f945c3a 100644 --- a/chrome/browser/media/router/BUILD.gn +++ b/chrome/browser/media/router/BUILD.gn
@@ -149,6 +149,7 @@ "providers/openscreen/network_service_quic_packet_writer.cc", "providers/openscreen/network_service_quic_packet_writer.h", "providers/openscreen/platform/logging.cc", + "providers/openscreen/platform/time.cc", ] configs +=
diff --git a/chrome/browser/media/router/providers/openscreen/platform/time.cc b/chrome/browser/media/router/providers/openscreen/platform/time.cc new file mode 100644 index 0000000..4a918323 --- /dev/null +++ b/chrome/browser/media/router/providers/openscreen/platform/time.cc
@@ -0,0 +1,38 @@ +// 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 "third_party/openscreen/src/platform/api/time.h" + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/time/time.h" + +using std::chrono::microseconds; +using std::chrono::seconds; + +namespace openscreen { +namespace platform { + +Clock::time_point Clock::now() noexcept { + // Open Screen requires at least 10,000 + // ticks per second, according to the docs. If IsHighResolution is false, the + // supplied resolution is much worse than that (potentially up to ~15.6ms). + if (UNLIKELY(!base::TimeTicks::IsHighResolution())) { + static bool need_to_log_once = true; + LOG_IF(ERROR, need_to_log_once) + << "Open Screen requires a high resolution clock to work properly."; + need_to_log_once = false; + } + + return Clock::time_point( + microseconds(base::TimeTicks::Now().since_origin().InMicroseconds())); +} + +std::chrono::seconds GetWallTimeSinceUnixEpoch() noexcept { + const auto delta = base::Time::Now() - base::Time::UnixEpoch(); + return seconds(delta.InSeconds()); +} + +} // namespace platform +} // namespace openscreen
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc index c34aa58..68123f2 100644 --- a/chrome/browser/metrics/ukm_browsertest.cc +++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -220,7 +220,11 @@ metrics::UnsentLogStore* log_store = ukm_service()->reporting_service_.ukm_log_store(); - EXPECT_FALSE(log_store->has_staged_log()); + if (log_store->has_staged_log()) { + // For testing purposes, we are examining the content of a staged log + // without ever sending the log, so discard any previously staged log. + log_store->DiscardStagedLog(); + } log_store->StageNextLog(); EXPECT_TRUE(log_store->has_staged_log()); @@ -1224,4 +1228,104 @@ UkmConsentParamBrowserTest, testing::Bool()); +// Verify that sources kept alive in-memory will be discarded by UKM service in +// one reporting cycle after the web contents are destroyed when the tab is +// closed or when the user navigated away in the same tab. +IN_PROC_BROWSER_TEST_P(UkmBrowserTest, EvictObsoleteSources) { + MetricsConsentOverride metrics_consent(true); + Profile* profile = ProfileManager::GetActiveUserProfile(); + std::unique_ptr<ProfileSyncServiceHarness> harness = + EnableSyncForProfile(profile); + Browser* sync_browser = CreateBrowser(profile); + + // Navigate to a URL in a new tab. + AddTabAtIndexToBrowser(sync_browser, 1, GURL("https://www.chromium.org"), + ui::PAGE_TRANSITION_TYPED, true); + ukm::SourceId source_id1 = ukm::GetSourceIdForWebContentsDocument( + sync_browser->tab_strip_model()->GetWebContentsAt(1)); + ukm::SourceId source_id2 = ukm::kInvalidSourceId; + + // The UKM report contains this newly-created source. + BuildAndStoreUkmLog(); + ukm::Report report = GetUkmReport(); + bool has_source_id1 = false; + bool has_source_id2 = false; + for (const auto& s : report.sources()) { + has_source_id1 |= s.id() == source_id1; + has_source_id2 |= s.id() == source_id2; + } + EXPECT_TRUE(has_source_id1); + EXPECT_FALSE(has_source_id2); + + // Navigate to a URL in another new tab. + AddTabAtIndexToBrowser(sync_browser, 2, GURL("https://www.google.com"), + ui::PAGE_TRANSITION_TYPED, true); + source_id2 = ukm::GetSourceIdForWebContentsDocument( + sync_browser->tab_strip_model()->GetWebContentsAt(2)); + + // The next report should again contain source 1 because the tab is still + // alive, and also source 2 associated to the new tab that has just been + // opened. + BuildAndStoreUkmLog(); + report = GetUkmReport(); + has_source_id1 = false; + has_source_id2 = false; + for (const auto& s : report.sources()) { + has_source_id1 |= s.id() == source_id1; + has_source_id2 |= s.id() == source_id2; + } + EXPECT_TRUE(has_source_id1); + EXPECT_TRUE(has_source_id2); + + // Close the tab corresponding to source 1, this should mark source 1 as + // obsolete. Next report will still contain source 1 because we might have + // associated entries before it was closed. + sync_browser->tab_strip_model()->CloseWebContentsAt( + 1, TabStripModel::CloseTypes::CLOSE_NONE); + + BuildAndStoreUkmLog(); + report = GetUkmReport(); + has_source_id1 = false; + has_source_id2 = false; + for (const auto& s : report.sources()) { + has_source_id1 |= s.id() == source_id1; + has_source_id2 |= s.id() == source_id2; + } + EXPECT_TRUE(has_source_id1); + EXPECT_TRUE(has_source_id2); + + // Navigate to a new URL in the current tab, this will mark source 2 that was + // in the current tab as obsolete. + ui_test_utils::NavigateToURL(sync_browser, GURL("https://www.wikipedia.org")); + + // The previous report was the last one that could potentially contain entries + // for source 1. Source 1 is thus no longer included in future reports. This + // report will still contain source 2 because we might have associated entries + // since the last report. + BuildAndStoreUkmLog(); + report = GetUkmReport(); + has_source_id1 = false; + has_source_id2 = false; + for (const auto& s : report.sources()) { + has_source_id1 |= s.id() == source_id1; + has_source_id2 |= s.id() == source_id2; + } + EXPECT_FALSE(has_source_id1); + EXPECT_TRUE(has_source_id2); + + // Neither source 1 or source 2 is alive anymore. + BuildAndStoreUkmLog(); + report = GetUkmReport(); + has_source_id1 = false; + has_source_id2 = false; + for (const auto& s : report.sources()) { + has_source_id1 |= s.id() == source_id1; + has_source_id2 |= s.id() == source_id2; + } + EXPECT_FALSE(has_source_id1); + EXPECT_FALSE(has_source_id2); + + CloseBrowserSynchronously(sync_browser); +} + } // namespace metrics
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc index a59bf93..49c3dd3 100644 --- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc +++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.cc
@@ -168,13 +168,11 @@ bool NaClBrowserDelegateImpl::MapUrlToLocalFilePath( const GURL& file_url, bool use_blocking_api, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) { #if BUILDFLAG(ENABLE_EXTENSIONS) - if (!extension_system) - return false; scoped_refptr<extensions::InfoMap> extension_info_map = - GetExtensionInfoMap(extension_system); + GetExtensionInfoMap(profile_directory); return extension_info_map->MapUrlToLocalFilePath( file_url, use_blocking_api, file_path); #else @@ -183,13 +181,11 @@ } bool NaClBrowserDelegateImpl::IsNonSfiModeAllowed( - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, const GURL& manifest_url) { #if BUILDFLAG(ENABLE_EXTENSIONS) - if (!extension_system) - return false; const extensions::ExtensionSet* extension_set = - &GetExtensionInfoMap(extension_system)->extensions(); + &GetExtensionInfoMap(profile_directory)->extensions(); return IsExtensionOrSharedModuleWhitelisted(manifest_url, extension_set, allowed_nonsfi_origins_); #else @@ -216,24 +212,13 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) scoped_refptr<extensions::InfoMap> NaClBrowserDelegateImpl::GetExtensionInfoMap( - extensions::ExtensionSystem* extension_system) { + const base::FilePath& profile_directory) { // Get the profile associated with the renderer. - DCHECK(extension_system); + Profile* profile = profile_manager_->GetProfileByPath(profile_directory); + DCHECK(profile); scoped_refptr<extensions::InfoMap> extension_info_map = - extension_system->info_map(); + extensions::ExtensionSystem::Get(profile)->info_map(); DCHECK(extension_info_map.get()); return extension_info_map; } #endif - -extensions::ExtensionSystem* NaClBrowserDelegateImpl::GetExtensionSystem( - const base::FilePath& profile_directory) { -#if BUILDFLAG(ENABLE_EXTENSIONS) - Profile* profile = profile_manager_->GetProfileByPath(profile_directory); - if (!profile) - return nullptr; - return extensions::ExtensionSystem::Get(profile); -#else - return nullptr; -#endif -}
diff --git a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h index bde0115a..fdcc379 100644 --- a/chrome/browser/nacl_host/nacl_browser_delegate_impl.h +++ b/chrome/browser/nacl_host/nacl_browser_delegate_impl.h
@@ -41,14 +41,12 @@ content::BrowserPpapiHost* ppapi_host) override; bool MapUrlToLocalFilePath(const GURL& url, bool is_blocking, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) override; void SetDebugPatterns(const std::string& debug_patterns) override; bool URLMatchesDebugPatterns(const GURL& manifest_url) override; - bool IsNonSfiModeAllowed(extensions::ExtensionSystem* extension_system, + bool IsNonSfiModeAllowed(const base::FilePath& profile_directory, const GURL& manifest_url) override; - extensions::ExtensionSystem* GetExtensionSystem( - const base::FilePath& profile_directory) override; private: // Creates a NaCl infobar and delegate for the given render process and view @@ -58,7 +56,7 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) scoped_refptr<extensions::InfoMap> GetExtensionInfoMap( - extensions::ExtensionSystem* extension_system); + const base::FilePath& profile_directory); std::vector<URLPattern> debug_patterns_; #endif
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc index f340365f..8010454 100644 --- a/chrome/browser/net/system_network_context_manager_browsertest.cc +++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -169,6 +169,18 @@ EXPECT_EQ(false, dns_over_https_servers->at(1)->use_post); servers.GetList().clear(); methods.GetList().clear(); + + // Test case with policy BuiltInDnsClientEnabled enabled. + local_state->Set(prefs::kDnsOverHttpsServers, servers); + local_state->Set(prefs::kDnsOverHttpsServerMethods, methods); + GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers); + EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled); + EXPECT_FALSE(dns_over_https_servers.has_value()); + local_state->Set(prefs::kBuiltInDnsClientEnabled, + base::Value(!async_dns_feature_enabled)); + GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers); + EXPECT_EQ(!async_dns_feature_enabled, stub_resolver_enabled); + EXPECT_FALSE(dns_over_https_servers.has_value()); } } // namespace
diff --git a/chrome/browser/notifications/proto/notification_data.proto b/chrome/browser/notifications/proto/notification_data.proto index 02d089f..0d2ba6a 100644 --- a/chrome/browser/notifications/proto/notification_data.proto +++ b/chrome/browser/notifications/proto/notification_data.proto
@@ -22,7 +22,7 @@ } // Stores data used to display a notification in the UI. -// Next tag: 7 +// Next tag: 6 message NotificationData { // Represents the button on the notification. // Next tag: 4 @@ -32,22 +32,19 @@ optional string id = 3; } - // Identifier of the notification. - optional string id = 1; - // Title of the notification. - optional string title = 2; + optional string title = 1; // Body text of the notification. - optional string message = 3; + optional string message = 2; // The unique identifiers of the icons on the notification. Each time we // should only load icons asynchronously into memory. - repeated string icon_uuid = 4; + repeated string icon_uuid = 3; // Custom data associated with a notification. - repeated CustomData custom_data = 5; + repeated CustomData custom_data = 4; // A list of buttons on the notification. - repeated Button buttons = 6; + repeated Button buttons = 5; }
diff --git a/chrome/browser/notifications/scheduler/display_agent_android.cc b/chrome/browser/notifications/scheduler/display_agent_android.cc index 86dff507..f996b57 100644 --- a/chrome/browser/notifications/scheduler/display_agent_android.cc +++ b/chrome/browser/notifications/scheduler/display_agent_android.cc
@@ -33,8 +33,10 @@ void JNI_DisplayAgent_OnContentClick( JNIEnv* env, const base::android::JavaParamRef<jobject>& j_profile, + jint j_client_type, const base::android::JavaParamRef<jstring>& j_guid) { GetUserActionHandler(j_profile)->OnClick( + static_cast<notifications::SchedulerClientType>(j_client_type), ConvertJavaStringToUTF8(env, j_guid)); } @@ -42,8 +44,10 @@ void JNI_DisplayAgent_OnDismiss( JNIEnv* env, const base::android::JavaParamRef<jobject>& j_profile, + jint j_client_type, const base::android::JavaParamRef<jstring>& j_guid) { GetUserActionHandler(j_profile)->OnDismiss( + static_cast<notifications::SchedulerClientType>(j_client_type), ConvertJavaStringToUTF8(env, j_guid)); } @@ -51,9 +55,11 @@ void JNI_DisplayAgent_OnActionButton( JNIEnv* env, const base::android::JavaParamRef<jobject>& j_profile, + jint j_client_type, const base::android::JavaParamRef<jstring>& j_guid, jint type) { GetUserActionHandler(j_profile)->OnActionClick( + static_cast<notifications::SchedulerClientType>(j_client_type), ConvertJavaStringToUTF8(env, j_guid), static_cast<notifications::ActionButtonType>(type)); } @@ -74,8 +80,7 @@ // Wrap button info. Retrieving class name in run time must be guarded with // test. auto java_notification_data = Java_DisplayAgent_buildNotificationData( - env, ConvertUTF8ToJavaString(env, notification_data->id), - ConvertUTF16ToJavaString(env, notification_data->title), + env, ConvertUTF16ToJavaString(env, notification_data->title), ConvertUTF16ToJavaString(env, notification_data->message), nullptr /*icon*/); @@ -87,7 +92,8 @@ } auto java_system_data = Java_DisplayAgent_buildSystemData( - env, ConvertUTF8ToJavaString(env, system_data->guid)); + env, static_cast<int>(system_data->type), + ConvertUTF8ToJavaString(env, system_data->guid)); ShowNotificationInternal(env, java_notification_data, java_system_data); }
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc index 26d55d2..3c1cc87 100644 --- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
@@ -109,19 +109,20 @@ std::move(callback).Run(std::move(detail)); } -void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) { - OnClickInternal(notification_id, true /*update_db*/); +void ImpressionHistoryTrackerImpl::OnClick(SchedulerClientType type, + const std::string& guid) { + OnClickInternal(guid, true /*update_db*/); } -void ImpressionHistoryTrackerImpl::OnActionClick( - const std::string& notification_id, - ActionButtonType button_type) { - OnButtonClickInternal(notification_id, button_type, true /*update_db*/); +void ImpressionHistoryTrackerImpl::OnActionClick(SchedulerClientType type, + const std::string& guid, + ActionButtonType button_type) { + OnButtonClickInternal(guid, button_type, true /*update_db*/); } -void ImpressionHistoryTrackerImpl::OnDismiss( - const std::string& notification_id) { - OnDismissInternal(notification_id, true /*update_db*/); +void ImpressionHistoryTrackerImpl::OnDismiss(SchedulerClientType type, + const std::string& guid) { + OnDismissInternal(guid, true /*update_db*/); } void ImpressionHistoryTrackerImpl::OnStoreInitialized(
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h index 2674808..80ff3c59 100644 --- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
@@ -95,10 +95,11 @@ void GetImpressionDetail( SchedulerClientType type, ImpressionDetail::ImpressionDetailCallback callback) override; - void OnClick(const std::string& notification_id) override; - void OnActionClick(const std::string& notification_id, + void OnClick(SchedulerClientType type, const std::string& guid) override; + void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) override; - void OnDismiss(const std::string& notification_id) override; + void OnDismiss(SchedulerClientType type, const std::string& guid) override; // Called after |store_| is initialized. void OnStoreInitialized(InitCallback callback, @@ -161,6 +162,7 @@ ClientStates client_states_; // Notification guid to Impression map. + // TODO(xingliu): Remove this. std::map<std::string, Impression*> impression_map_; // The storage that persists data.
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc index 7948d7c..e2142f8 100644 --- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
@@ -250,7 +250,7 @@ InitTrackerWithData(test_case); EXPECT_CALL(*store(), Update(_, _, _)).Times(0); EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0); - tracker()->OnClick(kGuid1); + tracker()->OnClick(SchedulerClientType::kTest1, kGuid1); VerifyClientStates(test_case); } @@ -303,9 +303,10 @@ // Trigger user action. if (GetParam().user_feedback == UserFeedback::kClick) - tracker()->OnClick(kGuid1); + tracker()->OnClick(SchedulerClientType::kTest1, kGuid1); else if (GetParam().button_type.has_value()) - tracker()->OnActionClick(kGuid1, GetParam().button_type.value()); + tracker()->OnActionClick(SchedulerClientType::kTest1, kGuid1, + GetParam().button_type.value()); VerifyClientStates(test_case); }
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc index 7dff59e..ca7c03a 100644 --- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
@@ -79,40 +79,39 @@ weak_ptr_factory_.GetWeakPtr(), task_time)); } -void InitAwareNotificationScheduler::OnClick( - const std::string& notification_id) { +void InitAwareNotificationScheduler::OnClick(SchedulerClientType type, + const std::string& guid) { if (IsReady()) { - impl_->OnClick(std::move(notification_id)); + impl_->OnClick(type, guid); return; } MaybeCacheClosure(base::BindOnce(&InitAwareNotificationScheduler::OnClick, - weak_ptr_factory_.GetWeakPtr(), - std::move(notification_id))); + weak_ptr_factory_.GetWeakPtr(), type, guid)); } void InitAwareNotificationScheduler::OnActionClick( - const std::string& notification_id, + SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) { if (IsReady()) { - impl_->OnActionClick(std::move(notification_id), button_type); + impl_->OnActionClick(type, guid, button_type); return; } - MaybeCacheClosure(base::BindOnce( - &InitAwareNotificationScheduler::OnActionClick, - weak_ptr_factory_.GetWeakPtr(), std::move(notification_id), button_type)); + MaybeCacheClosure( + base::BindOnce(&InitAwareNotificationScheduler::OnActionClick, + weak_ptr_factory_.GetWeakPtr(), type, guid, button_type)); } -void InitAwareNotificationScheduler::OnDismiss( - const std::string& notification_id) { +void InitAwareNotificationScheduler::OnDismiss(SchedulerClientType type, + const std::string& guid) { if (IsReady()) { - impl_->OnDismiss(std::move(notification_id)); + impl_->OnDismiss(type, guid); return; } MaybeCacheClosure(base::BindOnce(&InitAwareNotificationScheduler::OnDismiss, - weak_ptr_factory_.GetWeakPtr(), - std::move(notification_id))); + weak_ptr_factory_.GetWeakPtr(), type, guid)); } void InitAwareNotificationScheduler::OnInitialized(InitCallback init_callback,
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h index 7f9aedf..f50298fd 100644 --- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
@@ -41,10 +41,11 @@ void OnStartTask(SchedulerTaskTime task_time, TaskFinishedCallback callback) override; void OnStopTask(SchedulerTaskTime task_time) override; - void OnClick(const std::string& notification_id) override; - void OnActionClick(const std::string& notification_id, + void OnClick(SchedulerClientType type, const std::string& guid) override; + void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) override; - void OnDismiss(const std::string& notification_id) override; + void OnDismiss(SchedulerClientType type, const std::string& guid) override; // Called after initialization is done. void OnInitialized(InitCallback init_callback, bool success);
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc index d5d7d91..52eebe8 100644 --- a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
@@ -33,9 +33,10 @@ ImpressionDetail::ImpressionDetailCallback callback)); MOCK_METHOD2(OnStartTask, void(SchedulerTaskTime, TaskFinishedCallback)); MOCK_METHOD1(OnStopTask, void(SchedulerTaskTime)); - MOCK_METHOD1(OnClick, void(const std::string&)); - MOCK_METHOD2(OnActionClick, void(const std::string&, ActionButtonType)); - MOCK_METHOD1(OnDismiss, void(const std::string&)); + MOCK_METHOD2(OnClick, void(SchedulerClientType, const std::string&)); + MOCK_METHOD3(OnActionClick, + void(SchedulerClientType, const std::string&, ActionButtonType)); + MOCK_METHOD2(OnDismiss, void(SchedulerClientType, const std::string&)); private: DISALLOW_COPY_AND_ASSIGN(MockNotificationScheduler);
diff --git a/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.cc b/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.cc index 54f7d8a..6b74245 100644 --- a/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.cc +++ b/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.cc
@@ -37,14 +37,15 @@ void NoopNotificationScheduleService::OnStopTask(SchedulerTaskTime task_time) {} -void NoopNotificationScheduleService::OnClick( - const std::string& notification_id) {} +void NoopNotificationScheduleService::OnClick(SchedulerClientType type, + const std::string& guid) {} void NoopNotificationScheduleService::OnActionClick( - const std::string& notification_id, + SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) {} -void NoopNotificationScheduleService::OnDismiss( - const std::string& notification_id) {} +void NoopNotificationScheduleService::OnDismiss(SchedulerClientType type, + const std::string& guid) {} } // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.h b/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.h index 245a756..25630e8 100644 --- a/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.h +++ b/chrome/browser/notifications/scheduler/internal/noop_notification_schedule_service.h
@@ -5,6 +5,9 @@ #ifndef CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOOP_NOTIFICATION_SCHEDULE_SERVICE_H_ #define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOOP_NOTIFICATION_SCHEDULE_SERVICE_H_ +#include <memory> +#include <string> + #include "chrome/browser/notifications/scheduler/public/notification_schedule_service.h" #include "chrome/browser/notifications/scheduler/public/user_action_handler.h" @@ -36,10 +39,11 @@ void OnStopTask(SchedulerTaskTime task_time) override; // UserActionHandler implementation. - void OnClick(const std::string& notification_id) override; - void OnActionClick(const std::string& notification_id, + void OnClick(SchedulerClientType type, const std::string& guid) override; + void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) override; - void OnDismiss(const std::string& notification_id) override; + void OnDismiss(SchedulerClientType type, const std::string& guid) override; DISALLOW_COPY_AND_ASSIGN(NoopNotificationScheduleService); };
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc index ae15e3ebc..01feb35 100644 --- a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc +++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
@@ -58,20 +58,21 @@ scheduler_->OnStopTask(task_time); } -void NotificationScheduleServiceImpl::OnClick( - const std::string& notification_id) { - scheduler_->OnClick(notification_id); +void NotificationScheduleServiceImpl::OnClick(SchedulerClientType type, + const std::string& guid) { + scheduler_->OnClick(type, guid); } void NotificationScheduleServiceImpl::OnActionClick( - const std::string& notification_id, + SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) { - scheduler_->OnActionClick(notification_id, button_type); + scheduler_->OnActionClick(type, guid, button_type); } -void NotificationScheduleServiceImpl::OnDismiss( - const std::string& notification_id) { - scheduler_->OnDismiss(notification_id); +void NotificationScheduleServiceImpl::OnDismiss(SchedulerClientType type, + const std::string& guid) { + scheduler_->OnDismiss(type, guid); } void NotificationScheduleServiceImpl::OnInitialized(bool success) {
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h index 0830660c..46ffba6 100644 --- a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h +++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
@@ -45,10 +45,11 @@ void OnStopTask(SchedulerTaskTime task_time) override; // UserActionHandler implementation. - void OnClick(const std::string& notification_id) override; - void OnActionClick(const std::string& notification_id, + void OnClick(SchedulerClientType type, const std::string& guid) override; + void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) override; - void OnDismiss(const std::string& notification_id) override; + void OnDismiss(SchedulerClientType type, const std::string& guid) override; // Called after initialization is done. void OnInitialized(bool success);
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc index 172ba24..55d4e572 100644 --- a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc +++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -21,6 +21,7 @@ #include "chrome/browser/notifications/scheduler/internal/notification_entry.h" #include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h" #include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h" #include "chrome/browser/notifications/scheduler/public/display_agent.h" #include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" #include "chrome/browser/notifications/scheduler/public/notification_params.h" @@ -223,6 +224,7 @@ std::unique_ptr<NotificationData> updated_notification_data) { // Show the notification in UI. auto system_data = std::make_unique<DisplayAgent::SystemData>(); + system_data->type = entry->type; system_data->guid = entry->guid; context_->display_agent()->ShowNotification( std::move(updated_notification_data), std::move(system_data)); @@ -265,17 +267,52 @@ std::move(notifications), std::move(client_states), task_start_time_); } - void OnClick(const std::string& notification_id) override { - context_->impression_tracker()->OnClick(notification_id); + void OnClick(SchedulerClientType type, const std::string& guid) override { + context_->impression_tracker()->OnClick(type, guid); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&NotificationSchedulerImpl::NotifyClientAfterUserAction, + weak_ptr_factory_.GetWeakPtr(), UserActionType::kClick, + type, base::nullopt)); } - void OnActionClick(const std::string& notification_id, + void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) override { - context_->impression_tracker()->OnActionClick(notification_id, button_type); + context_->impression_tracker()->OnActionClick(type, guid, button_type); + + ButtonClickInfo button_info; + // TODO(xingliu): Plumb the button id from platform. + button_info.button_id = std::string(); + button_info.type = button_type; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&NotificationSchedulerImpl::NotifyClientAfterUserAction, + weak_ptr_factory_.GetWeakPtr(), + UserActionType::kButtonClick, type, + std::move(button_info))); } - void OnDismiss(const std::string& notification_id) override { - context_->impression_tracker()->OnDismiss(notification_id); + void OnDismiss(SchedulerClientType type, const std::string& guid) override { + context_->impression_tracker()->OnDismiss(type, guid); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&NotificationSchedulerImpl::NotifyClientAfterUserAction, + weak_ptr_factory_.GetWeakPtr(), UserActionType::kDismiss, + type, base::nullopt)); + } + + void NotifyClientAfterUserAction( + UserActionType action_type, + SchedulerClientType client_type, + base::Optional<ButtonClickInfo> button_info) { + auto* client = context_->client_registrar()->GetClient(client_type); + if (!client) + return; + + client->OnUserAction(action_type, std::move(button_info)); } std::unique_ptr<NotificationSchedulerContext> context_;
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc index e716adc..9d392f9 100644 --- a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc +++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
@@ -197,7 +197,6 @@ // Converts NotificationData to proto buffer type. void NotificationDataToProto(NotificationData* notification_data, proto::NotificationData* proto) { - proto->set_id(notification_data->id); proto->set_title(base::UTF16ToUTF8(notification_data->title)); proto->set_message(base::UTF16ToUTF8(notification_data->message)); for (const auto& pair : notification_data->custom_data) { @@ -217,7 +216,6 @@ // Converts NotificationData from proto buffer type. void NotificationDataFromProto(proto::NotificationData* proto, NotificationData* notification_data) { - notification_data->id = proto->id(); notification_data->title = base::UTF8ToUTF16(proto->title()); notification_data->message = base::UTF8ToUTF16(proto->message()); for (int i = 0; i < proto->custom_data_size(); ++i) {
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc index 0cccc36..b95c372b 100644 --- a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc +++ b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
@@ -185,7 +185,6 @@ TestNotificationEntryConversion(&entry); // Test notification data. - entry.notification_data.id = kGuid; entry.notification_data.title = base::UTF8ToUTF16("title"); entry.notification_data.message = base::UTF8ToUTF16("message"); entry.icons_uuid = {"icon_uuid_0", "icon_uuid_1"};
diff --git a/chrome/browser/notifications/scheduler/internal/webui_client.cc b/chrome/browser/notifications/scheduler/internal/webui_client.cc index d80af2d..28e5b36 100644 --- a/chrome/browser/notifications/scheduler/internal/webui_client.cc +++ b/chrome/browser/notifications/scheduler/internal/webui_client.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/notifications/scheduler/internal/webui_client.h" +#include <utility> + #include "base/logging.h" namespace notifications { @@ -24,7 +26,6 @@ } void WebUIClient::OnUserAction(UserActionType action_type, - const std::string& notification_id, base::Optional<ButtonClickInfo> button_info) { NOTIMPLEMENTED(); }
diff --git a/chrome/browser/notifications/scheduler/internal/webui_client.h b/chrome/browser/notifications/scheduler/internal/webui_client.h index d57540c..75a698e1 100644 --- a/chrome/browser/notifications/scheduler/internal/webui_client.h +++ b/chrome/browser/notifications/scheduler/internal/webui_client.h
@@ -27,7 +27,6 @@ void OnSchedulerInitialized(bool success, std::set<std::string> guids) override; void OnUserAction(UserActionType action_type, - const std::string& notification_id, base::Optional<ButtonClickInfo> button_info) override; DISALLOW_COPY_AND_ASSIGN(WebUIClient);
diff --git a/chrome/browser/notifications/scheduler/public/display_agent.h b/chrome/browser/notifications/scheduler/public/display_agent.h index a0f73b1..6b4cbed 100644 --- a/chrome/browser/notifications/scheduler/public/display_agent.h +++ b/chrome/browser/notifications/scheduler/public/display_agent.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_DISPLAY_AGENT_H_ #include <memory> +#include <string> #include "base/macros.h" #include "chrome/browser/notifications/scheduler/public/notification_data.h" @@ -19,6 +20,7 @@ // Contains data used used by the notification scheduling system internally to // build the notification. struct SystemData { + SchedulerClientType type; std::string guid; };
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.cc b/chrome/browser/notifications/scheduler/public/notification_data.cc index 28c0773..76578bc 100644 --- a/chrome/browser/notifications/scheduler/public/notification_data.cc +++ b/chrome/browser/notifications/scheduler/public/notification_data.cc
@@ -20,7 +20,7 @@ NotificationData::NotificationData(const NotificationData& other) = default; bool NotificationData::operator==(const NotificationData& other) const { - return id == other.id && title == other.title && message == other.message && + return title == other.title && message == other.message && icons.size() == other.icons.size() && custom_data == other.custom_data && buttons == other.buttons; }
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h index ade6de8..16c0f745 100644 --- a/chrome/browser/notifications/scheduler/public/notification_data.h +++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -44,11 +44,6 @@ bool operator==(const NotificationData& other) const; ~NotificationData(); - // The unique identifier of the notification. This is not used as the key for - // the database entry, but the id of other notification struct plumbed to - // the scheduler system. - std::string id; - // The title of the notification. base::string16 title;
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h index b1b7359..4da1fd8 100644 --- a/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h
@@ -42,7 +42,6 @@ // Called when the user interacts with the notification. virtual void OnUserAction(UserActionType action_type, - const std::string& notification_id, base::Optional<ButtonClickInfo> button_info) = 0; private:
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h index 24527d4a1..5cf07e6 100644 --- a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
@@ -24,6 +24,8 @@ }; // The type of a list of clients using the notification scheduler system. +// GENERATED_JAVA_ENUM_PACKAGE: ( +// org.chromium.chrome.browser.notifications.scheduler) enum class SchedulerClientType { // Test only values. kTest1 = -1,
diff --git a/chrome/browser/notifications/scheduler/public/user_action_handler.h b/chrome/browser/notifications/scheduler/public/user_action_handler.h index ca5fbb8..d5da8ee 100644 --- a/chrome/browser/notifications/scheduler/public/user_action_handler.h +++ b/chrome/browser/notifications/scheduler/public/user_action_handler.h
@@ -16,15 +16,17 @@ // Each event needs to provide an unique id of the notification shown. class UserActionHandler { public: - // Called when the user clicks on the notification. - virtual void OnClick(const std::string& notification_id) = 0; + // Called when the user clicks on the notification. |guid| is the internal id + // to track the notification persist to disk. + virtual void OnClick(SchedulerClientType type, const std::string& guid) = 0; // Called when the user clicks on a button on the notification. - virtual void OnActionClick(const std::string& notification_id, + virtual void OnActionClick(SchedulerClientType type, + const std::string& guid, ActionButtonType button_type) = 0; // Called when the user cancels or dismiss the notification. - virtual void OnDismiss(const std::string& notification_id) = 0; + virtual void OnDismiss(SchedulerClientType type, const std::string& guid) = 0; ~UserActionHandler() = default;
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.cc b/chrome/browser/notifications/scheduler/test/test_utils.cc index 721dd1f..c33b95b 100644 --- a/chrome/browser/notifications/scheduler/test/test_utils.cc +++ b/chrome/browser/notifications/scheduler/test/test_utils.cc
@@ -81,9 +81,9 @@ std::string DebugString(const NotificationData* data) { DCHECK(data); std::ostringstream stream; - stream << " Notification Data: \n id:" << data->id - << " \n title:" << data->title << "\n message:" << data->message; - stream << " \n custom data: "; + stream << " Notification Data:" + << " \n title:" << data->title << "\n message:" << data->message + << " \n custom data: "; for (const auto& pair : data->custom_data) stream << " key:" << pair.first << " , value:" << pair.second;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index b7101817..5f8892f 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -70,6 +70,7 @@ #include "chrome/browser/tracing/chrome_tracing_delegate.h" #include "chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h" #include "chrome/browser/ui/browser_ui_prefs.h" +#include "chrome/browser/ui/hats/hats_service.h" #include "chrome/browser/ui/navigation_correction_tab_observer.h" #include "chrome/browser/ui/network_profile_bubble.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" @@ -825,6 +826,7 @@ #if !defined(OS_ANDROID) browser_sync::ForeignSessionHandler::RegisterProfilePrefs(registry); first_run::RegisterProfilePrefs(registry); + HatsService::RegisterProfilePrefs(registry); InstantService::RegisterProfilePrefs(registry); SearchSuggestService::RegisterProfilePrefs(registry); gcm::GCMChannelStatusSyncer::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs b/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs index b381083..5ff31bc 100644 --- a/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs +++ b/chrome/browser/resources/chromeos/autoclick/autoclick_test.extjs
@@ -42,15 +42,12 @@ #include "base/callback.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" #include "chrome/common/extensions/extension_constants.h" -#include "ui/accessibility/accessibility_switches.h" `); }, /** @override */ testGenPreamble: function() { GEN(` - base::CommandLine::ForCurrentProcess()->AppendSwitch( - ::switches::kEnableExperimentalAccessibilityAutoclick); base::Closure load_cb = base::Bind(&chromeos::AccessibilityManager::EnableAutoclick, base::Unretained(chromeos::AccessibilityManager::Get()),
diff --git a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html index c5d7d1e..5aaccb29 100644 --- a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html +++ b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog.html
@@ -9,6 +9,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_components/chromeos/bluetooth_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/load_time_data.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> @@ -22,11 +23,7 @@ <dom-module id="bluetooth-pairing-dialog"> <template> - <style include="cr-shared-style"> - :host { - @apply --cr-page-host; - } - + <style include="cr-page-host-style cr-shared-style"> bluetooth-dialog::part(dialog) { border-radius: 0; height: 100%;
diff --git a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js index 0d49c2d..a96928e 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js +++ b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js
@@ -681,6 +681,8 @@ utterance = deleted + ', deleted'; } else if (deletedLen == 1) { utterance = deleted; + // Single-deleted characters should also use PERSONALITY_DELETED. + opt_personality = cvox.AbstractTts.PERSONALITY_DELETED; } if (autocompleteSuffix && utterance) {
diff --git a/chrome/browser/resources/chromeos/internet_config_dialog/internet_config_dialog.html b/chrome/browser/resources/chromeos/internet_config_dialog/internet_config_dialog.html index 369f5d4..932850b 100644 --- a/chrome/browser/resources/chromeos/internet_config_dialog/internet_config_dialog.html +++ b/chrome/browser/resources/chromeos/internet_config_dialog/internet_config_dialog.html
@@ -13,6 +13,7 @@ <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/load_time_data.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> @@ -27,11 +28,8 @@ <dom-module id="internet-config-dialog"> <template> - <style include="cr-shared-style network-shared iron-flex"> - :host { - @apply --cr-page-host; - } - + <style include="cr-page-host-style cr-shared-style network-shared + iron-flex"> cr-dialog::part(dialog) { border-radius: 0; height: 100%;
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html index 0430906aa..facf030 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -19,6 +19,7 @@ <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_network_icon.html"> <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_indicator.html"> @@ -36,11 +37,8 @@ <dom-module id="internet-detail-dialog"> <template> - <style include="cr-shared-style network-shared iron-flex"> - :host { - @apply --cr-page-host; - } - + <style include="cr-page-host-style cr-shared-style network-shared + iron-flex"> cr-policy-network-indicator { margin-inline-end: 10px; }
diff --git a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html index 4349147..e74e069 100644 --- a/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html +++ b/chrome/browser/resources/chromeos/set_time_dialog/set_time_dialog.html
@@ -11,6 +11,7 @@ <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/load_time_data.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/md_select_css.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="set_time_browser_proxy.html"> @@ -24,9 +25,8 @@ <body> <dom-module id="set-time-dialog"> <template> - <style include="cr-shared-style md-select"> + <style include="cr-page-host-style cr-shared-style md-select"> :host { - @apply --cr-page-host; user-select: none; }
diff --git a/chrome/browser/resources/downloads/manager.html b/chrome/browser/resources/downloads/manager.html index d736a5f..3415ecee 100644 --- a/chrome/browser/resources/downloads/manager.html +++ b/chrome/browser/resources/downloads/manager.html
@@ -7,6 +7,7 @@ <link rel="import" href="search_service.html"> <link rel="import" href="toolbar.html"> <link rel="import" href="chrome://resources/cr_components/managed_footnote/managed_footnote.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast_manager.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> @@ -21,7 +22,7 @@ <dom-module id="downloads-manager"> <template> - <style include="cr-shared-style cr-hidden-style"> + <style include="cr-page-host-style cr-shared-style cr-hidden-style"> :host { display: flex; flex: 1 0; @@ -29,8 +30,6 @@ height: 100%; overflow: hidden; z-index: 0; - - @apply --cr-page-host; } @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css index 9370c70..cd221779 100644 --- a/chrome/browser/resources/local_ntp/local_ntp.css +++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -735,26 +735,32 @@ width: 192px; } +@media (prefers-color-scheme: dark) { + .menu-option { + color: rgb(var(--GG200-rgb)); + } +} + html[dir=rtl] .menu-option { border-radius: 16px 0 0 16px; } .menu-option:hover, .menu-option:focus { - background-color: rgba(var(--GB900-rgb), .1); + background-color: rgba(var(--GG900-rgb), .1); } @media (prefers-color-scheme: dark) { .menu-option:hover, .menu-option:focus { - background-color: rgba(var(--GB200-rgb), .03); + background-color: rgba(var(--GG500-rgb), .12); } } .menu-option:active, .menu-option.selected { background-color: rgb(232, 240, 254); - color: rgb(var(--GB600-rgb)); + color: rgb(var(--GB700-rgb)); } @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/resources/management/management_ui.html b/chrome/browser/resources/management/management_ui.html index 2a87068..725e38c 100644 --- a/chrome/browser/resources/management/management_ui.html +++ b/chrome/browser/resources/management/management_ui.html
@@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> @@ -14,9 +15,9 @@ <dom-module id="management-ui"> <template> - <style include="cr-icons cr-hidden-style cr-shared-style"> + <style include="cr-icons cr-hidden-style cr-page-host-style + cr-shared-style"> :host { - @apply --cr-page-host; color: var(--cr-secondary-text-color); display: flex; flex-direction: column;
diff --git a/chrome/browser/resources/print_preview/ui/destination_dialog.html b/chrome/browser/resources/print_preview/ui/destination_dialog.html index 35b8851..a941dae 100644 --- a/chrome/browser/resources/print_preview/ui/destination_dialog.html +++ b/chrome/browser/resources/print_preview/ui/destination_dialog.html
@@ -165,9 +165,6 @@ <option value="">$i18n{addAccountTitle}</option> </select> </div> - <!-- Note: Autofocus is not used below, as due to a Blink issue it is - not possible to test refocusing this element in tests if autofocus is - used. autofocus can be restored when this issue is resolved. --> <print-preview-search-box id="searchBox" label="$i18n{searchBoxPlaceholder}" search-query="{{searchQuery_}}"> </print-preview-search-box>
diff --git a/chrome/browser/resources/print_preview/ui/destination_dialog.js b/chrome/browser/resources/print_preview/ui/destination_dialog.js index fc733942..5c30560 100644 --- a/chrome/browser/resources/print_preview/ui/destination_dialog.js +++ b/chrome/browser/resources/print_preview/ui/destination_dialog.js
@@ -287,6 +287,11 @@ show: function() { this.$.dialog.showModal(); + // Note: Manually focusing here instead of using autofocus, as it is + // currently not possible to validate refocusing of the search input if + // autofocus is used. See https://crbug.com/985637 and + // https://crbug.com/985636. Autofocus can be restored when one or both of + // these issues are resolved. this.$.searchBox.focus(); this.loadingDestinations_ = this.destinationStore === undefined || this.destinationStore.isPrintDestinationSearchInProgress;
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html index edbceefc..5c4aea1 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html +++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -27,7 +27,7 @@ -webkit-tap-highlight-color: transparent; } - /* Override --cr-selectable-focus mixin. */ + /* Override CSS from cr_elements/shared_style_css.html. */ [selectable]:focus, [selectable] > :focus { background-color: transparent;
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html index 9440932..3c35d5c 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html +++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
@@ -3,6 +3,7 @@ <link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> @@ -22,9 +23,8 @@ <dom-module id="os-settings-ui"> <template> - <style include="settings-shared"> + <style include="cr-page-host-style settings-shared"> :host { - @apply --cr-page-host; @apply --layout-fit; display: flex; flex-direction: column;
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html index 923f59a2..9d428404 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_page_host_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> @@ -25,9 +26,8 @@ <dom-module id="settings-ui"> <template> - <style include="settings-shared"> + <style include="cr-page-host-style settings-shared"> :host { - @apply --cr-page-host; @apply --layout-fit; display: flex; flex-direction: column;
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc index 111d559..56648e7e 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
@@ -17,6 +17,7 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" +#include "base/metrics/sparse_histogram.h" #include "base/optional.h" #include "base/rand_util.h" #include "base/strings/strcat.h" @@ -27,6 +28,7 @@ #include "base/task/post_task.h" #include "base/unguessable_token.h" #include "base/win/win_util.h" +#include "base/win/windows_types.h" #include "components/chrome_cleaner/public/constants/constants.h" #include "components/chrome_cleaner/public/constants/result_codes.h" #include "content/public/browser/browser_task_traits.h" @@ -41,8 +43,22 @@ using content::BrowserThread; using CleanerProcessDelegate = ChromePromptChannel::CleanerProcessDelegate; +constexpr char ChromePromptChannelProtobuf::kErrorHistogramName[] = + "SoftwareReporter.Cleaner.ChromePromptChannelError"; + namespace { +template <typename ErrorType> +void WriteStatusErrorCodeToHistogram( + ChromePromptChannelProtobuf::ErrorCategory category, + ErrorType error_type) { + base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( + ChromePromptChannelProtobuf::kErrorHistogramName, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add( + ChromePromptChannelProtobuf::GetErrorCodeInt(category, error_type)); +} + class CleanerProcessWrapper : public CleanerProcessDelegate { public: explicit CleanerProcessWrapper(const base::Process& process) @@ -212,13 +228,22 @@ // Since the field is only 1 byte it's not possible to have a short read. static_assert(sizeof(version) == 1, "version field must be readable in 1 byte"); + if (!::ReadFile(request_read_handle, &version, sizeof(version), &bytes_read, nullptr)) { + WriteStatusErrorCodeToHistogram( + ChromePromptChannelProtobuf::ErrorCategory::kReadVersionWinError, + logging::GetLastSystemErrorCode()); PLOG(ERROR) << "Failed to read protocol version"; return; } + CHECK_EQ(bytes_read, sizeof(version)); if (version != 1) { + WriteStatusErrorCodeToHistogram( + ChromePromptChannelProtobuf::ErrorCategory::kCustomError, + ChromePromptChannelProtobuf::CustomErrors::kWrongHandshakeVersion); + LOG(ERROR) << "Cleaner requested unsupported version " << version; return; }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h index 17a5e97..51c6429 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
@@ -83,6 +83,10 @@ virtual void ConnectToCleaner( std::unique_ptr<CleanerProcessDelegate> cleaner_process) = 0; + scoped_refptr<base::SequencedTaskRunner> task_runner() const { + return task_runner_; + } + protected: base::OnceClosure on_connection_closed_; std::unique_ptr<ChromePromptActions> actions_; @@ -132,6 +136,47 @@ // a pipe. class ChromePromptChannelProtobuf : public ChromePromptChannel { public: + static const char kErrorHistogramName[]; + + // Values from this enum will serve as the high bits of the histogram values. + // We will be able to use them to separate the errors by category if we ever + // need to analyze them. + enum class ErrorCategory : uint16_t { + kCustomError = 1, + kReadVersionWinError = 2, + kReadRequestLengthWinError = 3, + kReadRequestWinError = 4, + }; + + // Code that describes the error precisely. + enum class CustomErrors : uint16_t { + kWrongHandshakeVersion = 1, + kRequestLengthShortRead = 2, + kRequestShortRead = 3, + }; + + static int32_t GetErrorCodeInt(ErrorCategory category, + CustomErrors error_code) { + return GetErrorCodeInt(category, static_cast<uint16_t>(error_code)); + } + + static int32_t GetErrorCodeInt(ErrorCategory category, uint16_t error_code) { + // We are storing the bits of the category and error_code in the return type + // without caring about the numerical values. Make sure everything fits. + using CategoryNumType = std::underlying_type<ErrorCategory>::type; + using ErrorCodeType = decltype(error_code); + static_assert(std::is_same<CategoryNumType, ErrorCodeType>::value, + "Category and error code types need to be the same."); + static_assert( + sizeof(int32_t) == sizeof(CategoryNumType) + sizeof(ErrorCodeType), + "Values won't fit in return type."); + + int32_t category_and_code = static_cast<CategoryNumType>(category); + category_and_code <<= sizeof(category) * CHAR_BIT; + category_and_code |= error_code; + return category_and_code; + } + ChromePromptChannelProtobuf( base::OnceClosure on_connection_closed, std::unique_ptr<ChromePromptActions> actions,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc new file mode 100644 index 0000000..ee17414 --- /dev/null +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
@@ -0,0 +1,302 @@ +// 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/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h" + +#include <windows.h> + +#include <memory> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/process/launch.h" +#include "base/process/process.h" +#include "base/run_loop.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/task/post_task.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_task_environment.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" +#include "chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h" +#include "components/chrome_cleaner/public/constants/constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::StrictMock; + +namespace safe_browsing { +namespace { + +static constexpr uint8_t kVersion = 1U; + +// Get the error category from a histogram sample. +ChromePromptChannelProtobuf::ErrorCategory SampleToCategory( + int histogram_sample) { + return static_cast<ChromePromptChannelProtobuf::ErrorCategory>( + histogram_sample >> (sizeof(uint16_t) * CHAR_BIT)); +} + +class MockCleanerProcessDelegate + : public ChromePromptChannel::CleanerProcessDelegate { + public: + MOCK_CONST_METHOD0(Handle, base::ProcessHandle()); + MOCK_CONST_METHOD0(TerminateOnError, void()); +}; + +class ChromePromptChannelProtobufTest : public ::testing::Test { + public: + using ChromePromptChannelPtr = + std::unique_ptr<ChromePromptChannelProtobuf, base::OnTaskRunnerDeleter>; + + ChromePromptChannelProtobufTest() = default; + + ~ChromePromptChannelProtobufTest() override = default; + + void SetUp() override { + auto task_runner = base::CreateSequencedTaskRunner({base::MayBlock()}); + channel_ = ChromePromptChannelPtr( + new ChromePromptChannelProtobuf( + /*on_connection_closed=*/run_loop_.QuitClosure(), + std::make_unique<ChromePromptActions>( + /*extension_service=*/nullptr, + /*on_prompt_user=*/base::DoNothing()), + task_runner), + base::OnTaskRunnerDeleter(task_runner)); + + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + base::HandlesToInheritVector handles_to_inherit; + ASSERT_TRUE( + channel_->PrepareForCleaner(&command_line, &handles_to_inherit)); + + // Instead of spawning a cleaner process, extract the prompt handles from + // the command-line. Duplicate them so that we retain ownership if + // ChromePromptChannelProtobuf closes them. + response_read_handle_ = DuplicateHandleFromCommandLine( + command_line, chrome_cleaner::kChromeReadHandleSwitch); + ASSERT_TRUE(response_read_handle_.IsValid()); + request_write_handle_ = DuplicateHandleFromCommandLine( + command_line, chrome_cleaner::kChromeWriteHandleSwitch); + ASSERT_TRUE(request_write_handle_.IsValid()); + + mock_cleaner_process_ = + std::make_unique<StrictMock<MockCleanerProcessDelegate>>(); + } + + // Used to wait for the disconnect. This ensures that work done on other + // sequences is complete. + void WaitForDisconnect() { + // When channel_ closes the quit closure of run_loop_ is invoked. + run_loop_.Run(); + } + + void SetupCommunicationFailure() { + // Expect a call to TerminateOnError since the execution will fail. + // Have that call trigger a closing of cleaner handles which ensures reads + // start failing. + EXPECT_CALL(*mock_cleaner_process_, TerminateOnError) + .WillOnce(InvokeWithoutArgs( + this, &ChromePromptChannelProtobufTest::CloseCleanerHandles)); + } + + template <typename T> + void ExpectUniqueSample(ChromePromptChannelProtobuf::ErrorCategory category, + T error, + int count = 1) { + histogram_tester_.ExpectUniqueSample( + ChromePromptChannelProtobuf::kErrorHistogramName, + ChromePromptChannelProtobuf::GetErrorCodeInt(category, error), count); + } + + void ExpectHistogramEmpty() { + histogram_tester_.ExpectTotalCount( + ChromePromptChannelProtobuf::kErrorHistogramName, 0); + } + + // This is used when we want to validate that an operation failed a certain + // number of times without needing to know the specific error code. Not to be + // used with CustomErrors as those are well-known. + void ExpectCategoryErrorCount( + ChromePromptChannelProtobuf::ErrorCategory category, + uint32_t expected_count) { + std::vector<base::Bucket> buckets = histogram_tester_.GetAllSamples( + ChromePromptChannelProtobuf::kErrorHistogramName); + + uint32_t samples_found = 0; + for (const base::Bucket& bucket : buckets) { + if (SampleToCategory(bucket.min) == category) { + samples_found += bucket.count; + } + } + + EXPECT_EQ(expected_count, samples_found); + } + + // Closes the cleaner process pipe handles to simulate the cleaner process + // exiting. + void CloseCleanerHandles() { + response_read_handle_.Close(); + request_write_handle_.Close(); + } + + void PostCloseCleanerHandles() { + channel_->task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&ChromePromptChannelProtobufTest::CloseCleanerHandles, + base::Unretained(this))); + } + + void WriteVersion(uint8_t version) { + DWORD bytes_written = 0; + ASSERT_TRUE(::WriteFile(request_write_handle_.Get(), &version, + sizeof(version), &bytes_written, nullptr)); + ASSERT_EQ(bytes_written, sizeof(version)); + } + + // Writes the version to the pipe without blocking the main test thread. + void PostWriteVersion(uint8_t version) { + channel_->task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&ChromePromptChannelProtobufTest::WriteVersion, + base::Unretained(this), version)); + } + + void ExpectReadFails() { + DWORD bytes_read = 0; + char c = 0; + EXPECT_FALSE( + ::ReadFile(response_read_handle_.Get(), &c, 1, &bytes_read, nullptr)); + } + + protected: + base::win::ScopedHandle DuplicateHandleFromCommandLine( + const base::CommandLine& command_line, + const std::string& handle_switch) { + uint32_t handle_value = 0; + if (!base::StringToUint(command_line.GetSwitchValueNative(handle_switch), + &handle_value)) { + LOG(ERROR) << handle_switch << " not found on commandline"; + return base::win::ScopedHandle(); + } + HANDLE handle = base::win::Uint32ToHandle(handle_value); + HANDLE duplicate_handle; + if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), + &duplicate_handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + PLOG(ERROR) << "Failed to duplicate handle from " << handle_switch; + return base::win::ScopedHandle(); + } + return base::win::ScopedHandle(duplicate_handle); + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + base::RunLoop run_loop_; + ChromePromptChannelPtr channel_ = + ChromePromptChannelPtr(nullptr, base::OnTaskRunnerDeleter(nullptr)); + base::win::ScopedHandle response_read_handle_; + base::win::ScopedHandle request_write_handle_; + + std::unique_ptr<StrictMock<MockCleanerProcessDelegate>> mock_cleaner_process_; + base::HistogramTester histogram_tester_; +}; + +TEST_F(ChromePromptChannelProtobufTest, PipeInfo) { + DWORD read_pipe_flags = 0; + DWORD read_pipe_max_instances = 0; + ASSERT_TRUE(::GetNamedPipeInfo(response_read_handle_.Get(), &read_pipe_flags, + nullptr, nullptr, &read_pipe_max_instances)); + EXPECT_TRUE(read_pipe_flags & PIPE_TYPE_MESSAGE); + EXPECT_EQ(read_pipe_max_instances, 1UL); + + DWORD write_pipe_flags = 0; + DWORD write_pipe_max_instances = 0; + ASSERT_TRUE(::GetNamedPipeInfo(request_write_handle_.Get(), &write_pipe_flags, + nullptr, nullptr, &write_pipe_max_instances)); + EXPECT_TRUE(write_pipe_flags & PIPE_TYPE_MESSAGE); + EXPECT_EQ(write_pipe_max_instances, 1UL); +} + +TEST_F(ChromePromptChannelProtobufTest, ImmediateExit) { + EXPECT_CALL(*mock_cleaner_process_, TerminateOnError).Times(1); + channel_->ConnectToCleaner(std::move(mock_cleaner_process_)); + + // Simulate the cleaner exiting without writing anything. + PostCloseCleanerHandles(); + + WaitForDisconnect(); + ExpectReadFails(); +} + +TEST_F(ChromePromptChannelProtobufTest, VersionIsTooLarge) { + SetupCommunicationFailure(); + channel_->ConnectToCleaner(std::move(mock_cleaner_process_)); + + // Invalid version + PostWriteVersion(128); + WaitForDisconnect(); + + // We expect the the handshake to have failed because of the version. + ExpectUniqueSample( + ChromePromptChannelProtobuf::ErrorCategory::kCustomError, + ChromePromptChannelProtobuf::CustomErrors::kWrongHandshakeVersion); + + ExpectReadFails(); +} + +TEST_F(ChromePromptChannelProtobufTest, VersionIsZero) { + SetupCommunicationFailure(); + channel_->ConnectToCleaner(std::move(mock_cleaner_process_)); + + // Invalid version + PostWriteVersion(0U); + WaitForDisconnect(); + + // We expect the the handshake to have failed because of the version. + ExpectUniqueSample( + ChromePromptChannelProtobuf::ErrorCategory::kCustomError, + ChromePromptChannelProtobuf::CustomErrors::kWrongHandshakeVersion); + ExpectReadFails(); +} + +TEST_F(ChromePromptChannelProtobufTest, ExitAfterVersion) { + SetupCommunicationFailure(); + channel_->ConnectToCleaner(std::move(mock_cleaner_process_)); + + // Write version 1. + PostWriteVersion(kVersion); + + // Simulate the cleaner exiting after writing the version. + PostCloseCleanerHandles(); + + WaitForDisconnect(); + + // There were no errors so the histogram should be empty + // TODO(crbug.com/969139): We should be getting a kReadRequestLengthWinError + // here. + ExpectHistogramEmpty(); + + ExpectReadFails(); +} + +TEST_F(ChromePromptChannelProtobufTest, ExitBeforeVersion) { + SetupCommunicationFailure(); + channel_->ConnectToCleaner(std::move(mock_cleaner_process_)); + + // Simulate the cleaner exiting before writing the version. + PostCloseCleanerHandles(); + WaitForDisconnect(); + + ExpectCategoryErrorCount( + ChromePromptChannelProtobuf::ErrorCategory::kReadVersionWinError, 1); + ExpectReadFails(); +} + +} // namespace +} // namespace safe_browsing
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service.cc b/chrome/browser/search/chrome_colors/chrome_colors_service.cc index c889004..4905ec3 100644 --- a/chrome/browser/search/chrome_colors/chrome_colors_service.cc +++ b/chrome/browser/search/chrome_colors/chrome_colors_service.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/search/chrome_colors/chrome_colors_service.h" +#include "base/metrics/user_metrics.h" #include "chrome/browser/themes/theme_service_factory.h" namespace chrome_colors { @@ -25,8 +26,10 @@ } void ChromeColorsService::RevertThemeChangesForTab(content::WebContents* tab) { - if (dialog_tab_ == tab) + if (dialog_tab_ == tab) { + RecordAction(base::UserMetricsAction("ChromeColors_TabClosed")); RevertThemeChanges(); + } } void ChromeColorsService::RevertThemeChanges() {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 4a58cf3..a5d85c1 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1336,6 +1336,7 @@ "zoom/chrome_zoom_level_prefs.h", ] deps += [ + "//base/util/values:values_util", "//chrome/app/vector_icons", "//chrome/browser:theme_properties", "//chrome/browser/media/router",
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc index b5e55ef..6edb67a 100644 --- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc +++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
@@ -104,6 +104,11 @@ controller_->IsRTL()); } +base::Optional<int32_t> AutofillPopupViewAndroid::GetAxUniqueId() { + NOTIMPLEMENTED() << "See https://crbug.com/985927"; + return base::nullopt; +} + void AutofillPopupViewAndroid::SuggestionSelected( JNIEnv* env, const JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.h b/chrome/browser/ui/android/autofill/autofill_popup_view_android.h index 5028762..768e565 100644 --- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.h +++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.h
@@ -48,6 +48,7 @@ void OnSelectedRowChanged(base::Optional<int> previous_row_selection, base::Optional<int> current_row_selection) override; void OnSuggestionsChanged() override; + base::Optional<int32_t> GetAxUniqueId() override; private: friend class AutofillPopupView;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc index 69daceb..10dd7a5 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -15,16 +15,22 @@ #include "base/optional.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "chrome/browser/accessibility/accessibility_state_utils.h" #include "chrome/browser/ui/autofill/autofill_popup_view.h" #include "components/autofill/content/browser/content_autofill_driver.h" #include "components/autofill/core/browser/ui/autofill_popup_delegate.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "content/public/browser/native_web_keyboard_event.h" +#include "ui/accessibility/ax_active_popup.h" +#include "ui/accessibility/ax_tree_id.h" +#include "ui/accessibility/ax_tree_manager_map.h" +#include "ui/accessibility/platform/ax_platform_node.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" +#include "ui/views/accessibility/view_accessibility.h" #if defined(OS_ANDROID) #include "chrome/browser/autofill/manual_filling_controller_impl.h" @@ -130,6 +136,11 @@ !suggestions.empty()); #endif view_->Show(); + + // We only fire the event when a new popup shows. We do not fire the + // event when suggestions changed. + FireControlsChangedEvent(true); + if (autoselect_first_suggestion) SetSelectedLine(0); } else { @@ -578,10 +589,45 @@ /*has_suggestions=*/false); #endif - if (view_) + if (view_) { + // We need to fire the event while view is not deleted yet. + FireControlsChangedEvent(false); view_->Hide(); + } delete this; } +void AutofillPopupControllerImpl::FireControlsChangedEvent(bool is_show) { + if (!accessibility_state_utils::IsScreenReaderEnabled()) + return; + DCHECK(view_); + + // Retrieve the ax tree id associated with the current web contents. + ui::AXTreeID tree_id = delegate_->GetAutofillDriver()->GetAxTreeId(); + ui::AXTreeManager* ax_tree_manager = + ui::AXTreeManagerMap::GetInstance().GetManager(tree_id); + + // Retrieve the ax node id associated with the current web contents' element + // that has a controller relation to the current autofill popup. + int32_t node_id = static_cast<AutofillExternalDelegate*>(delegate_.get()) + ->GetWebContentsPopupControllerAxId(); + + // We can only raise controls changed accessibility event when we have a valid + // ax tree and an ax node associated with the ax tree for the popup + // controller, and a valid ax unique id for the popup controllee. + if (!ax_tree_manager || !ax_tree_manager->GetDelegate(tree_id, node_id) || + !view_->GetAxUniqueId()) + return; + + if (is_show) + ui::SetActivePopupAxUniqueId(view_->GetAxUniqueId()); + else + ui::ClearActivePopupAxUniqueId(); + + ax_tree_manager->GetDelegate(tree_id, node_id) + ->GetFromNodeID(node_id) + ->NotifyAccessibilityEvent(ax::mojom::Event::kControlsChanged); +} + } // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h index c57e3e64..5c3aa39f 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -155,6 +155,11 @@ // Hides |view_| unless it is null and then deletes |this|. void HideViewAndDie(); + // Raise an accessibility event to indicate the controls relation of the + // form control of the popup and popup itself has changed based on the popup's + // show or hide action. + void FireControlsChangedEvent(bool is_show); + friend class AutofillPopupControllerUnitTest; void SetViewForTesting(AutofillPopupView* view) { view_ = view; }
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc index f7bf0247..82e2959 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc +++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -85,6 +85,7 @@ void(base::Optional<int> previous_row_selection, base::Optional<int> current_row_selection)); MOCK_METHOD0(OnSuggestionsChanged, void()); + MOCK_METHOD0(GetAxUniqueId, base::Optional<int32_t>()); private: DISALLOW_COPY_AND_ASSIGN(MockAutofillPopupView);
diff --git a/chrome/browser/ui/autofill/autofill_popup_view.h b/chrome/browser/ui/autofill/autofill_popup_view.h index 86251e6..4232670 100644 --- a/chrome/browser/ui/autofill/autofill_popup_view.h +++ b/chrome/browser/ui/autofill/autofill_popup_view.h
@@ -33,6 +33,9 @@ // Refreshes the position and redraws popup when suggestions change. virtual void OnSuggestionsChanged() = 0; + // Return the autofill popup view's ax unique id. + virtual base::Optional<int32_t> GetAxUniqueId() = 0; + // Factory function for creating the view. static AutofillPopupView* Create(AutofillPopupController* controller);
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc index 547c776..0b955619 100644 --- a/chrome/browser/ui/hats/hats_service.cc +++ b/chrome/browser/ui/hats/hats_service.cc
@@ -8,12 +8,19 @@ #include "base/metrics/field_trial_params.h" #include "base/rand_util.h" +#include "base/util/values/values_util.h" +#include "base/values.h" +#include "base/version.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_features.h" +#include "chrome/common/pref_names.h" #include "components/metrics_services_manager/metrics_services_manager.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/version_info/version_info.h" namespace { // Which survey we're triggering @@ -23,21 +30,40 @@ constexpr char kHatsSurveyEnSiteID[] = "en_site_id"; -constexpr char kHatsSurveyTriggerDefault[] = "test"; - constexpr double kHatsSurveyProbabilityDefault = 0; constexpr char kHatsSurveyEnSiteIDDefault[] = "z4cctguzopq5x2ftal6vdgjrui"; constexpr char kHatsSurveyTriggerSatisfaction[] = "satisfaction"; +constexpr base::TimeDelta kMinimumTimeBetweenSurveyStarts = + base::TimeDelta::FromDays(60); + +// Preferences Data Model +// The kHatsSurveyMetadata pref points to a dictionary. +// The valid keys and value types for this dictionary are as follows: +// [trigger].last_major_version ---> Integer +// [trigger].last_survey_started_time ---> Time + +std::string GetMajorVersionPath(const std::string& trigger) { + return trigger + ".last_major_version"; +} + +std::string GetLastSurveyStartedTime(const std::string& trigger) { + return trigger + ".last_survey_started_time"; +} + } // namespace -HatsService::HatsService() - : trigger_(base::FeatureParam<std::string>( +HatsService::SurveyMetadata::SurveyMetadata() = default; +HatsService::SurveyMetadata::~SurveyMetadata() = default; + +HatsService::HatsService(Profile* profile) + : profile_(profile), + trigger_(base::FeatureParam<std::string>( &features::kHappinessTrackingSurveysForDesktop, kHatsSurveyTrigger, - kHatsSurveyTriggerDefault) + "") .Get()), probability_(base::FeatureParam<double>( &features::kHappinessTrackingSurveysForDesktop, @@ -50,11 +76,51 @@ kHatsSurveyEnSiteIDDefault) .Get()) {} +// static +void HatsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(prefs::kHatsSurveyMetadata); +} + void HatsService::LaunchSatisfactionSurvey() { if (ShouldShowSurvey(kHatsSurveyTriggerSatisfaction)) { Browser* browser = chrome::FindLastActive(); - if (browser && browser->is_type_tabbed()) + if (browser && browser->is_type_tabbed()) { browser->window()->ShowHatsBubbleFromAppMenuButton(); + + DictionaryPrefUpdate update(profile_->GetPrefs(), + prefs::kHatsSurveyMetadata); + base::DictionaryValue* pref_data = update.Get(); + pref_data->SetIntPath(GetMajorVersionPath(kHatsSurveyTriggerSatisfaction), + version_info::GetVersion().components()[0]); + pref_data->SetPath( + GetLastSurveyStartedTime(kHatsSurveyTriggerSatisfaction), + util::TimeToValue(base::Time::Now())); + } + } +} + +void HatsService::SetSurveyMetadataForTesting( + const HatsService::SurveyMetadata& metadata) { + const std::string& trigger = kHatsSurveyTriggerSatisfaction; + DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kHatsSurveyMetadata); + base::DictionaryValue* pref_data = update.Get(); + if (!metadata.last_major_version.has_value() && + !metadata.last_survey_started_time.has_value()) { + pref_data->RemovePath(trigger); + } + + if (metadata.last_major_version.has_value()) { + pref_data->SetIntPath(GetMajorVersionPath(trigger), + *metadata.last_major_version); + } else { + pref_data->RemovePath(GetMajorVersionPath(trigger)); + } + + if (metadata.last_survey_started_time.has_value()) { + pref_data->SetPath(GetLastSurveyStartedTime(trigger), + util::TimeToValue(*metadata.last_survey_started_time)); + } else { + pref_data->RemovePath(GetLastSurveyStartedTime(trigger)); } } @@ -64,17 +130,29 @@ if (!consent_given) return false; - if ((trigger_ == trigger || trigger_ == kHatsSurveyTriggerDefault) && - !launch_hats_) { - if (base::RandDouble() < probability_) { - // we only want to ever show hats once per profile. - launch_hats_ = true; - return true; - } - // TODO add pref checks to avoid too many surveys for a single profile + const base::DictionaryValue* pref_data = + profile_->GetPrefs()->GetDictionary(prefs::kHatsSurveyMetadata); + base::Optional<int> last_major_version = + pref_data->FindIntPath(GetMajorVersionPath(trigger)); + if (last_major_version.has_value() && + static_cast<uint32_t>(*last_major_version) == + version_info::GetVersion().components()[0]) { + return false; } - return false; -} -// static -bool HatsService::launch_hats_ = false; + base::Time now = base::Time::Now(); + + base::Optional<base::Time> last_survey_started_time = + util::ValueToTime(pref_data->FindPath(GetLastSurveyStartedTime(trigger))); + if (last_survey_started_time.has_value()) { + base::TimeDelta elapsed_time_since_last_start = + now - *last_survey_started_time; + if (elapsed_time_since_last_start < kMinimumTimeBetweenSurveyStarts) + return false; + } + + if (trigger_ != trigger) + return false; + + return base::RandDouble() < probability_; +}
diff --git a/chrome/browser/ui/hats/hats_service.h b/chrome/browser/ui/hats/hats_service.h index 0583373..74b4d5fa 100644 --- a/chrome/browser/ui/hats/hats_service.h +++ b/chrome/browser/ui/hats/hats_service.h
@@ -8,14 +8,29 @@ #include <string> #include "base/macros.h" +#include "base/optional.h" +#include "base/time/time.h" #include "components/keyed_service/core/keyed_service.h" +class PrefRegistrySimple; +class Profile; + // This class provides the client side logic for determining if a // survey should be shown for any trigger based on input from a finch // configuration. It is created on a per profile basis. class HatsService : public KeyedService { public: - HatsService(); + struct SurveyMetadata { + SurveyMetadata(); + ~SurveyMetadata(); + + base::Optional<int> last_major_version; + base::Optional<base::Time> last_survey_started_time; + }; + + explicit HatsService(Profile* profile); + + static void RegisterProfilePrefs(PrefRegistrySimple* registry); // This is the public function that will launch the "satisfaction" survey if // it's appropriate. @@ -24,13 +39,14 @@ // Returns the en-us site ID for the HaTS survey. const std::string& en_site_id() const { return en_site_id_; } + void SetSurveyMetadataForTesting(const SurveyMetadata& metadata); + private: // This returns true is the survey trigger specified should be shown. bool ShouldShowSurvey(const std::string& trigger) const; - // a temporary flag to ensure that hats is not launched multiple times - // TODO: replace with pref lookup - static bool launch_hats_; + // Profile associated with this service. + Profile* const profile_; // Trigger string identifier. const std::string trigger_;
diff --git a/chrome/browser/ui/hats/hats_service_browsertest.cc b/chrome/browser/ui/hats/hats_service_browsertest.cc index d3fbf210..7125d84 100644 --- a/chrome/browser/ui/hats/hats_service_browsertest.cc +++ b/chrome/browser/ui/hats/hats_service_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/metrics/user_metrics.h" #include "base/optional.h" #include "base/test/scoped_feature_list.h" +#include "base/version.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "chrome/browser/ui/browser.h" @@ -15,6 +16,7 @@ #include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/metrics_services_manager/metrics_services_manager.h" +#include "components/version_info/version_info.h" #include "content/public/test/browser_test.h" namespace { @@ -64,6 +66,8 @@ bool HatsDialogShowRequested() { return hats_dialog_show_requested_; } + void ResetHatsDialogShowRequested() { hats_dialog_show_requested_ = false; } + private: void OnHatsDialogShow(const std::string& action) { if (action == "HatsBubble.Show") { @@ -135,7 +139,12 @@ HatsServiceFactory::GetForProfile(browser()->profile(), false)); scoped_feature_list_.InitAndEnableFeatureWithParameters( features::kHappinessTrackingSurveysForDesktop, - {{"probability", "1.000"}}); + {{"probability", "1.000"}, {"survey", "satisfaction"}}); + GetHatsService()->SetSurveyMetadataForTesting({}); + } + + void TearDownOnMainThread() override { + GetHatsService()->SetSurveyMetadataForTesting({}); } base::test::ScopedFeatureList scoped_feature_list_; @@ -160,3 +169,46 @@ GetHatsService()->LaunchSatisfactionSurvey(); EXPECT_TRUE(HatsDialogShowRequested()); } + +IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, + DoubleShowOnlyResultsInOneShow) { + SetMetricsConsent(true); + ASSERT_TRUE( + g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven()); + + GetHatsService()->LaunchSatisfactionSurvey(); + EXPECT_TRUE(HatsDialogShowRequested()); + ResetHatsDialogShowRequested(); + + GetHatsService()->LaunchSatisfactionSurvey(); + EXPECT_FALSE(HatsDialogShowRequested()); +} + +IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, SameMajorVersionNoShow) { + SetMetricsConsent(true); + HatsService::SurveyMetadata metadata; + metadata.last_major_version = version_info::GetVersion().components()[0]; + GetHatsService()->SetSurveyMetadataForTesting(metadata); + GetHatsService()->LaunchSatisfactionSurvey(); + EXPECT_FALSE(HatsDialogShowRequested()); +} + +IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, DifferentMajorVersionShow) { + SetMetricsConsent(true); + HatsService::SurveyMetadata metadata; + metadata.last_major_version = 42; + ASSERT_NE(42u, version_info::GetVersion().components()[0]); + GetHatsService()->SetSurveyMetadataForTesting(metadata); + GetHatsService()->LaunchSatisfactionSurvey(); + EXPECT_TRUE(HatsDialogShowRequested()); +} + +IN_PROC_BROWSER_TEST_F(HatsServiceProbabilityOne, + SurveyStartedBeforeRequiredElapsedTimeNoShow) { + SetMetricsConsent(true); + HatsService::SurveyMetadata metadata; + metadata.last_survey_started_time = base::Time::Now(); + GetHatsService()->SetSurveyMetadataForTesting(metadata); + GetHatsService()->LaunchSatisfactionSurvey(); + EXPECT_FALSE(HatsDialogShowRequested()); +}
diff --git a/chrome/browser/ui/hats/hats_service_factory.cc b/chrome/browser/ui/hats/hats_service_factory.cc index c55cab8..0c9fbc0 100644 --- a/chrome/browser/ui/hats/hats_service_factory.cc +++ b/chrome/browser/ui/hats/hats_service_factory.cc
@@ -36,7 +36,7 @@ return (profile->IsOffTheRecord() || profile->IsGuestSession() || profile->IsSystemProfile()) ? nullptr - : new HatsService(); + : new HatsService(profile); } HatsServiceFactory::~HatsServiceFactory() = default;
diff --git a/chrome/browser/ui/startup/startup_browser_creator.h b/chrome/browser/ui/startup/startup_browser_creator.h index 12d29fc6..1d232be 100644 --- a/chrome/browser/ui/startup/startup_browser_creator.h +++ b/chrome/browser/ui/startup/startup_browser_creator.h
@@ -26,20 +26,6 @@ // initialize the profile. class StartupBrowserCreator { public: - // The type of page to be shown in a tab when the user is being welcomed back - // to Chrome. - enum class WelcomeBackPage { - kNone, -#if defined(OS_WIN) - // chrome://welcome-win10/ if Chrome's default browser UX may be shown; - // otherwise, see kWelcomeStandard. - // TODO(hcarmona): deprecate this enum. - kWelcomeWin10, -#endif - // chrome://welcome/ if sign-in is allowed; otherwise, none. - kWelcomeStandard, - }; - typedef std::vector<Profile*> Profiles; StartupBrowserCreator(); @@ -53,10 +39,10 @@ // tab before other tabs (e.g., those from session restore). This is used for // specific launches via retention experiments for which no URLs are provided // on the command line. No "welcome back" page is shown to supervised users. - void set_welcome_back_page(WelcomeBackPage welcome_back_page) { + void set_welcome_back_page(bool welcome_back_page) { welcome_back_page_ = welcome_back_page; } - WelcomeBackPage welcome_back_page() const { return welcome_back_page_; } + bool welcome_back_page() const { return welcome_back_page_; } // This function is equivalent to ProcessCommandLine but should only be // called during actual process startup. @@ -197,7 +183,7 @@ std::vector<GURL> first_run_tabs_; // The page to be shown in a tab when welcoming a user back to Chrome. - WelcomeBackPage welcome_back_page_ = WelcomeBackPage::kNone; + bool welcome_back_page_ = false; // True if the set-as-default dialog has been explicitly suppressed. // This information is used to allow the default browser prompt to show on
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc index 6d180f6..542fcd9e 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1366,9 +1366,8 @@ ASSERT_EQ(0U, BrowserList::GetInstance()->size()); } - void StartBrowser(StartupBrowserCreator::WelcomeBackPage welcome_back_page, - PolicyVariant variant) { - browser_creator_.set_welcome_back_page(welcome_back_page); + void StartBrowser(PolicyVariant variant) { + browser_creator_.set_welcome_back_page(true); if (variant) { policy::PolicyMap values; @@ -1405,37 +1404,9 @@ policy::MockConfigurationPolicyProvider provider_; }; -#if defined(OS_WIN) -IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, - WelcomeBackWin10NoPolicy) { - ASSERT_NO_FATAL_FAILURE(StartBrowser( - StartupBrowserCreator::WelcomeBackPage::kWelcomeWin10, PolicyVariant())); - ExpectUrlInBrowserAtPosition(StartupTabProviderImpl::GetWelcomePageUrl(false), - 0); -} - -IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, - WelcomeBackWin10MandatoryPolicy) { - ASSERT_NO_FATAL_FAILURE( - StartBrowser(StartupBrowserCreator::WelcomeBackPage::kWelcomeWin10, - PolicyVariant(policy::POLICY_LEVEL_MANDATORY))); - ExpectUrlInBrowserAtPosition(GURL("http://managed.site.com/"), 0); -} - -IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, - WelcomeBackWin10RecommendedPolicy) { - ASSERT_NO_FATAL_FAILURE( - StartBrowser(StartupBrowserCreator::WelcomeBackPage::kWelcomeWin10, - PolicyVariant(policy::POLICY_LEVEL_RECOMMENDED))); - ExpectUrlInBrowserAtPosition(GURL("http://managed.site.com/"), 0); -} -#endif // defined(OS_WIN) - IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, WelcomeBackStandardNoPolicy) { - ASSERT_NO_FATAL_FAILURE( - StartBrowser(StartupBrowserCreator::WelcomeBackPage::kWelcomeStandard, - PolicyVariant())); + ASSERT_NO_FATAL_FAILURE(StartBrowser(PolicyVariant())); ExpectUrlInBrowserAtPosition(StartupTabProviderImpl::GetWelcomePageUrl(false), 0); } @@ -1443,16 +1414,14 @@ IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, WelcomeBackStandardMandatoryPolicy) { ASSERT_NO_FATAL_FAILURE( - StartBrowser(StartupBrowserCreator::WelcomeBackPage::kWelcomeStandard, - PolicyVariant(policy::POLICY_LEVEL_MANDATORY))); + StartBrowser(PolicyVariant(policy::POLICY_LEVEL_MANDATORY))); ExpectUrlInBrowserAtPosition(GURL("http://managed.site.com/"), 0); } IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorWelcomeBackTest, WelcomeBackStandardRecommendedPolicy) { ASSERT_NO_FATAL_FAILURE( - StartBrowser(StartupBrowserCreator::WelcomeBackPage::kWelcomeStandard, - PolicyVariant(policy::POLICY_LEVEL_RECOMMENDED))); + StartBrowser(PolicyVariant(policy::POLICY_LEVEL_RECOMMENDED))); ExpectUrlInBrowserAtPosition(GURL("http://managed.site.com/"), 0); }
diff --git a/chrome/browser/ui/startup/startup_tab_provider.cc b/chrome/browser/ui/startup/startup_tab_provider.cc index af53321..a1555df 100644 --- a/chrome/browser/ui/startup/startup_tab_provider.cc +++ b/chrome/browser/ui/startup/startup_tab_provider.cc
@@ -78,20 +78,10 @@ StartupTabs tabs; if (!process_startup || !browser_creator) return tabs; - switch (browser_creator->welcome_back_page()) { - case StartupBrowserCreator::WelcomeBackPage::kNone: - break; -#if defined(OS_WIN) - case StartupBrowserCreator::WelcomeBackPage::kWelcomeWin10: - // TODO(hcarmona): delete kWelcomeWin10. - FALLTHROUGH; -#endif // defined(OS_WIN) - case StartupBrowserCreator::WelcomeBackPage::kWelcomeStandard: - if (CanShowWelcome(profile->IsSyncAllowed(), profile->IsSupervised(), - signin_util::IsForceSigninEnabled())) { - tabs.emplace_back(GetWelcomePageUrl(false), false); - } - break; + if (browser_creator->welcome_back_page() && + CanShowWelcome(profile->IsSyncAllowed(), profile->IsSupervised(), + signin_util::IsForceSigninEnabled())) { + tabs.emplace_back(GetWelcomePageUrl(false), false); } return tabs; }
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.cc b/chrome/browser/ui/toolbar/back_forward_menu_model.cc index e52e2e7..2f7eca93 100644 --- a/chrome/browser/ui/toolbar/back_forward_menu_model.cc +++ b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
@@ -426,11 +426,11 @@ NavigationEntry* BackForwardMenuModel::GetNavigationEntry(int index) const { int controller_index = MenuIndexToNavEntryIndex(index); NavigationController& controller = GetWebContents()->GetController(); - if (controller_index >= 0 && controller_index < controller.GetEntryCount()) - return controller.GetEntryAtIndex(controller_index); - NOTREACHED(); - return nullptr; + DCHECK_GE(controller_index, 0); + DCHECK_LT(controller_index, controller.GetEntryCount()); + + return controller.GetEntryAtIndex(controller_index); } std::string BackForwardMenuModel::BuildActionName(
diff --git a/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc b/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc new file mode 100644 index 0000000..280752d --- /dev/null +++ b/chrome/browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc
@@ -0,0 +1,134 @@ +// 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 "base/logging.h" +#include "base/win/scoped_variant.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/accessibility/uia_accessibility_event_waiter.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/accessibility_notification_waiter.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test_utils.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/request_handler_util.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/accessibility_switches.h" +#include "ui/accessibility/ax_tree.h" +#include "ui/accessibility/ax_tree_id.h" +#include "ui/accessibility/ax_tree_manager_map.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/keycodes/dom/dom_key.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "url/gurl.h" + +namespace autofill { + +class AutofillAccessibilityWinBrowserTest : public InProcessBrowserTest { + public: + AutofillAccessibilityWinBrowserTest() = default; + + protected: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + ASSERT_TRUE(embedded_test_server()->Start()); + + content::WebContents* web_contents = GetWebContents(); + web_contents->SetAccessibilityMode(ui::kAXModeComplete); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(switches::kEnableExperimentalUIAutomation); + } + + content::WebContents* GetWebContents() const { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + HWND GetWebPageHwnd() const { + return browser() + ->window() + ->GetNativeWindow() + ->GetHost() + ->GetAcceleratedWidget(); + } + + // Show drop down based on the element id. + void ShowDropdown(const std::string& field_id) { + std::string js("document.getElementById('" + field_id + "').focus();"); + ASSERT_TRUE(ExecuteScript(GetWebContents(), js)); + SendKeyToPage(GetWebContents(), ui::DomKey::ARROW_DOWN); + } + + void SendKeyToPage(content::WebContents* web_contents, const ui::DomKey key) { + ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key); + ui::DomCode code = ui::UsLayoutKeyboardCodeToDomCode(key_code); + SimulateKeyPress(web_contents, key, code, key_code, false, false, false, + false); + } + + private: + DISALLOW_COPY_AND_ASSIGN(AutofillAccessibilityWinBrowserTest); +}; + +IN_PROC_BROWSER_TEST_F(AutofillAccessibilityWinBrowserTest, + AutofillPopupControllerFor) { + content::AccessibilityNotificationWaiter waiter( + GetWebContents(), ui::kAXModeComplete, ax::mojom::Event::kLoadComplete); + ui_test_utils::NavigateToURL( + browser(), + embedded_test_server()->GetURL("/accessibility/input_datalist.html")); + waiter.WaitForNotification(); + + base::win::ScopedVariant result_variant; + + content::FindAccessibilityNodeCriteria find_criteria; + find_criteria.role = ax::mojom::Role::kTextFieldWithComboBox; + + // The autofill popup of the form input element has not shown yet. The form + // input element is the controller for the checkbox as indicated by the form + // input element's |aria-controls| attribute. + UiaGetPropertyValueVtArrayVtUnknownValidate( + UIA_ControllerForPropertyId, + FindAccessibilityNode(GetWebContents(), find_criteria), {"checkbox"}); + + UiaAccessibilityWaiterInfo info = { + GetWebPageHwnd(), base::ASCIIToUTF16("combobox"), + base::ASCIIToUTF16("input"), ax::mojom::Event::kControlsChanged}; + + std::unique_ptr<UiaAccessibilityEventWaiter> control_waiter = + std::make_unique<UiaAccessibilityEventWaiter>(info); + // Show popup and wait for UIA_ControllerForPropertyId event. + ShowDropdown("datalist"); + control_waiter->Wait(); + + // The autofill popup of the form input element is showing. The form input + // element is the controller for the checkbox and autofill popup as + // indicated by the form input element's |aria-controls| attribute and the + // existing popup. + UiaGetPropertyValueVtArrayVtUnknownValidate( + UIA_ControllerForPropertyId, + FindAccessibilityNode(GetWebContents(), find_criteria), + {"checkbox", "Autofill"}); + + control_waiter.reset(new UiaAccessibilityEventWaiter(info)); + // Hide popup and wait for UIA_ControllerForPropertyId event. + SendKeyToPage(GetWebContents(), ui::DomKey::TAB); + control_waiter->Wait(); + + // The autofill popup of the form input element is hidden. The form + // input element is the controller for the checkbox as indicated by the form + // input element's |aria-controls| attribute. + UiaGetPropertyValueVtArrayVtUnknownValidate( + UIA_ControllerForPropertyId, + FindAccessibilityNode(GetWebContents(), find_criteria), {"checkbox"}); +} + +} // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc index 0bed6a64..04a47d6 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -880,6 +880,11 @@ DoUpdateBoundsAndRedrawPopup(); } +base::Optional<int32_t> AutofillPopupViewNativeViews::GetAxUniqueId() { + return base::Optional<int32_t>( + AutofillPopupBaseView::GetViewAccessibility().GetUniqueId()); +} + void AutofillPopupViewNativeViews::CreateChildViews() { RemoveAllChildViews(true /* delete_children */); rows_.clear();
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h index 54ec3645..3efbb19 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -77,6 +77,7 @@ // AutofillPopupView: void Show() override; void Hide() override; + base::Optional<int32_t> GetAxUniqueId() override; // AutofillPopupBaseView: // TODO(crbug.com/831603): Remove these overrides and the corresponding
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view_browsertest.cc b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view_browsertest.cc index 10ccc6e..b9f3006 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view_browsertest.cc +++ b/chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view_browsertest.cc
@@ -2,15 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h" +#include "chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h" #include "base/files/file_path.h" +#include "base/test/bind_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/grit/generated_resources.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/window/dialog_client_view.h" -class NativeFileSystemRestrictedDirectoryDialogViewTest +class NativeFileSystemDirectoryAccessConfirmationViewTest : public DialogBrowserTest { public: void SetUpOnMainThread() override { @@ -21,31 +24,73 @@ // are referenced from the Chrome binary. auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance(); shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TITLE, - base::ASCIIToUTF16("Can't save to this folder")); + IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE, + base::ASCIIToUTF16("Let site read this folder?")); shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TEXT, - base::ASCIIToUTF16("$1 can't save your changes to this folder because " - "it contains system files.")); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON, - base::ASCIIToUTF16("Choose a different folder")); + IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TEXT, + base::ASCIIToUTF16("$1 will be able to read all files in the following " + "folder. This site can see any changes to the " + "folder only while this tab is open.")); } // DialogBrowserTest: void ShowUi(const std::string& name) override { - NativeFileSystemRestrictedDirectoryDialogView::ShowDialog( + widget_ = NativeFileSystemDirectoryAccessConfirmationView::ShowDialog( kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")), - base::DoNothing(), + base::BindLambdaForTesting([&](PermissionAction result) { + callback_called_ = true; + callback_result_ = result; + }), browser()->tab_strip_model()->GetActiveWebContents()); } protected: const url::Origin kTestOrigin = url::Origin::Create(GURL("https://example.com")); + + views::Widget* widget_ = nullptr; + + bool callback_called_ = false; + PermissionAction callback_result_ = PermissionAction::IGNORED; }; -IN_PROC_BROWSER_TEST_F(NativeFileSystemRestrictedDirectoryDialogViewTest, +IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, + AcceptIsntDefaultFocused) { + ShowUi(std::string()); + EXPECT_NE(widget_->client_view()->AsDialogClientView()->ok_button(), + widget_->GetFocusManager()->GetFocusedView()); + widget_->Close(); + base::RunLoop().RunUntilIdle(); +} + +IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, + AcceptRunsCallback) { + ShowUi(std::string()); + widget_->client_view()->AsDialogClientView()->AcceptWindow(); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(PermissionAction::GRANTED, callback_result_); + base::RunLoop().RunUntilIdle(); +} + +IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, + CancelRunsCallback) { + ShowUi(std::string()); + widget_->client_view()->AsDialogClientView()->CancelWindow(); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); + base::RunLoop().RunUntilIdle(); +} + +IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, + CancelsWhenClosed) { + ShowUi(std::string()); + widget_->Close(); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); + base::RunLoop().RunUntilIdle(); +} + +IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, InvokeUi_default) { ShowAndVerifyUi(); }
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc index e542dba..9b05b9e 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc +++ b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view.cc
@@ -32,32 +32,10 @@ provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT), provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL))); - AddChildView(native_file_system_ui_helper::CreateOriginLabel( + AddChildView(native_file_system_ui_helper::CreateOriginPathLabel( is_directory ? IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT : IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT, - origin, CONTEXT_BODY_TEXT_SMALL)); - - auto file_label_container = std::make_unique<views::View>(); - int indent = - provider->GetDistanceMetric(DISTANCE_SUBSECTION_HORIZONTAL_INDENT); - file_label_container->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::Orientation::kHorizontal, - gfx::Insets(/*vertical=*/0, indent), - provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL))); - auto icon = std::make_unique<views::ImageView>(); - const gfx::VectorIcon& vector_id = - is_directory ? vector_icons::kFolderOpenIcon - : vector_icons::kInsertDriveFileOutlineIcon; - const SkColor icon_color = icon->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_DefaultIconColor); - icon->SetImage(gfx::CreateVectorIcon(vector_id, /*dip_size=*/18, icon_color)); - file_label_container->AddChildView(std::move(icon)); - auto file_label = std::make_unique<views::Label>(path.LossyDisplayName(), - CONTEXT_BODY_TEXT_SMALL, - STYLE_EMPHASIZED_SECONDARY); - file_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); - file_label_container->AddChildView(std::move(file_label)); - AddChildView(std::move(file_label_container)); + origin, path, CONTEXT_BODY_TEXT_SMALL)); } NativeFileSystemPermissionView::~NativeFileSystemPermissionView() { @@ -79,9 +57,13 @@ } base::string16 NativeFileSystemPermissionView::GetWindowTitle() const { - return l10n_util::GetStringUTF16( - is_directory_ ? IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE - : IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE); + if (is_directory_) { + return l10n_util::GetStringUTF16( + IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE); + } + return l10n_util::GetStringFUTF16( + IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE, + path_.BaseName().LossyDisplayName()); } int NativeFileSystemPermissionView::GetDefaultDialogButton() const {
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc index 5a258dc..d7ff53e 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc +++ b/chrome/browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc
@@ -9,46 +9,28 @@ #include "chrome/browser/permissions/permission_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/test/test_browser_dialog.h" -#include "chrome/grit/generated_resources.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/window/dialog_client_view.h" class NativeFileSystemPermissionViewTest : public DialogBrowserTest { public: - void SetUpOnMainThread() override { - // Release builds may strip out unused string resources when - // enable_resource_whitelist_generation is enabled. Manually override the - // strings needed by the dialog to ensure they are available for tests. - // TODO(https://crbug.com/979659): Remove these overrides once the strings - // are referenced from the Chrome binary. - auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance(); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TITLE, - base::ASCIIToUTF16("Edit file?")); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TITLE, - base::ASCIIToUTF16("Edit folder?")); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_FILE_TEXT, - base::ASCIIToUTF16( - "This will allow $1 to save or edit the following file(s) on your " - "device. Only do this if you trust the app.")); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_DIRECTORY_TEXT, - base::ASCIIToUTF16( - "This will allow $1 to make changes to the following folder on " - "your device. Only do this if you trust the app.")); - shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_WRITE_PERMISSION_ALLOW_TEXT, - base::ASCIIToUTF16("Allow")); - } - // DialogBrowserTest: void ShowUi(const std::string& name) override { + base::FilePath path; + bool is_directory = false; + if (name == "LongFileName") { + path = base::FilePath(FILE_PATH_LITERAL( + "/foo/bar/Some Really Really Really Really Long File Name.txt")); + } else if (name == "Folder") { + path = base::FilePath(FILE_PATH_LITERAL("/bar/MyProject")); + is_directory = true; + } else if (name == "default") { + path = base::FilePath(FILE_PATH_LITERAL("/foo/README.txt")); + } else { + NOTREACHED() << "Unimplemented test: " << name; + } widget_ = NativeFileSystemPermissionView::ShowDialog( - kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")), - /*is_directory=*/false, + kTestOrigin, path, is_directory, base::BindLambdaForTesting([&](PermissionAction result) { callback_called_ = true; callback_result_ = result; @@ -68,7 +50,7 @@ IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, AcceptIsntDefaultFocused) { - ShowUi(std::string()); + ShowUi("default"); EXPECT_NE(widget_->client_view()->AsDialogClientView()->ok_button(), widget_->GetFocusManager()->GetFocusedView()); widget_->Close(); @@ -76,7 +58,7 @@ } IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, AcceptRunsCallback) { - ShowUi(std::string()); + ShowUi("default"); widget_->client_view()->AsDialogClientView()->AcceptWindow(); EXPECT_TRUE(callback_called_); EXPECT_EQ(PermissionAction::GRANTED, callback_result_); @@ -84,7 +66,7 @@ } IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, CancelRunsCallback) { - ShowUi(std::string()); + ShowUi("default"); widget_->client_view()->AsDialogClientView()->CancelWindow(); EXPECT_TRUE(callback_called_); EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); @@ -92,7 +74,7 @@ } IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, CancelsWhenClosed) { - ShowUi(std::string()); + ShowUi("default"); widget_->Close(); EXPECT_TRUE(callback_called_); EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); @@ -102,3 +84,12 @@ IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, InvokeUi_default) { ShowAndVerifyUi(); } + +IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, + InvokeUi_LongFileName) { + ShowAndVerifyUi(); +} + +IN_PROC_BROWSER_TEST_F(NativeFileSystemPermissionViewTest, InvokeUi_Folder) { + ShowAndVerifyUi(); +}
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc index b9f3006..10ccc6e 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc
@@ -2,18 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h" +#include "chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h" #include "base/files/file_path.h" -#include "base/test/bind_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/grit/generated_resources.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/views/controls/button/label_button.h" -#include "ui/views/window/dialog_client_view.h" -class NativeFileSystemDirectoryAccessConfirmationViewTest +class NativeFileSystemRestrictedDirectoryDialogViewTest : public DialogBrowserTest { public: void SetUpOnMainThread() override { @@ -24,73 +21,31 @@ // are referenced from the Chrome binary. auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance(); shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE, - base::ASCIIToUTF16("Let site read this folder?")); + IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TITLE, + base::ASCIIToUTF16("Can't save to this folder")); shared_resource_bundle.OverrideLocaleStringResource( - IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TEXT, - base::ASCIIToUTF16("$1 will be able to read all files in the following " - "folder. This site can see any changes to the " - "folder only while this tab is open.")); + IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TEXT, + base::ASCIIToUTF16("$1 can't save your changes to this folder because " + "it contains system files.")); + shared_resource_bundle.OverrideLocaleStringResource( + IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON, + base::ASCIIToUTF16("Choose a different folder")); } // DialogBrowserTest: void ShowUi(const std::string& name) override { - widget_ = NativeFileSystemDirectoryAccessConfirmationView::ShowDialog( + NativeFileSystemRestrictedDirectoryDialogView::ShowDialog( kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")), - base::BindLambdaForTesting([&](PermissionAction result) { - callback_called_ = true; - callback_result_ = result; - }), + base::DoNothing(), browser()->tab_strip_model()->GetActiveWebContents()); } protected: const url::Origin kTestOrigin = url::Origin::Create(GURL("https://example.com")); - - views::Widget* widget_ = nullptr; - - bool callback_called_ = false; - PermissionAction callback_result_ = PermissionAction::IGNORED; }; -IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, - AcceptIsntDefaultFocused) { - ShowUi(std::string()); - EXPECT_NE(widget_->client_view()->AsDialogClientView()->ok_button(), - widget_->GetFocusManager()->GetFocusedView()); - widget_->Close(); - base::RunLoop().RunUntilIdle(); -} - -IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, - AcceptRunsCallback) { - ShowUi(std::string()); - widget_->client_view()->AsDialogClientView()->AcceptWindow(); - EXPECT_TRUE(callback_called_); - EXPECT_EQ(PermissionAction::GRANTED, callback_result_); - base::RunLoop().RunUntilIdle(); -} - -IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, - CancelRunsCallback) { - ShowUi(std::string()); - widget_->client_view()->AsDialogClientView()->CancelWindow(); - EXPECT_TRUE(callback_called_); - EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); - base::RunLoop().RunUntilIdle(); -} - -IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, - CancelsWhenClosed) { - ShowUi(std::string()); - widget_->Close(); - EXPECT_TRUE(callback_called_); - EXPECT_EQ(PermissionAction::DISMISSED, callback_result_); - base::RunLoop().RunUntilIdle(); -} - -IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest, +IN_PROC_BROWSER_TEST_F(NativeFileSystemRestrictedDirectoryDialogViewTest, InvokeUi_default) { ShowAndVerifyUi(); }
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc index 9ec7722d..b0c8d14 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc +++ b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.h" +#include "base/files/file_path.h" #include "chrome/browser/ui/views/chrome_typography.h" #include "components/url_formatter/elide_url.h" #include "ui/base/l10n/l10n_util.h" @@ -32,4 +33,38 @@ return label; } +std::unique_ptr<views::View> CreateOriginPathLabel(int message_id, + const url::Origin& origin, + const base::FilePath& path, + int text_context) { + base::string16 formatted_origin = + url_formatter::FormatOriginForSecurityDisplay( + origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC); + base::string16 formatted_path = path.BaseName().LossyDisplayName(); + std::vector<size_t> offsets; + auto label = std::make_unique<views::StyledLabel>( + l10n_util::GetStringFUTF16(message_id, formatted_origin, formatted_path, + &offsets), + nullptr); + DCHECK_EQ(2U, offsets.size()); + + label->SetTextContext(text_context); + label->SetDefaultTextStyle(STYLE_SECONDARY); + label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + + views::StyledLabel::RangeStyleInfo origin_style; + origin_style.text_style = STYLE_EMPHASIZED_SECONDARY; + label->AddStyleRange( + gfx::Range(offsets[0], offsets[0] + formatted_origin.length()), + origin_style); + + views::StyledLabel::RangeStyleInfo path_style; + path_style.text_style = STYLE_EMPHASIZED_SECONDARY; + path_style.tooltip = path.LossyDisplayName(); + label->AddStyleRange( + gfx::Range(offsets[1], offsets[1] + formatted_path.length()), path_style); + + return label; +} + } // namespace native_file_system_ui_helper
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.h b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.h index ee726c1..aaed03c 100644 --- a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.h +++ b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.h
@@ -7,6 +7,10 @@ #include <memory> +namespace base { +class FilePath; +} + namespace url { class Origin; } @@ -23,6 +27,13 @@ const url::Origin& origin, int text_context); +// Creates and returns a label where the place holders are replaced with +// |origin| and |path|, while formatting the origin and path as emphasized text. +std::unique_ptr<views::View> CreateOriginPathLabel(int message_id, + const url::Origin& origin, + const base::FilePath& path, + int text_context); + } // namespace native_file_system_ui_helper #endif // CHROME_BROWSER_UI_VIEWS_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_UI_HELPERS_H_
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index c72880a..d1031ab 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -848,8 +848,12 @@ // URL. Otherwise, we would have to use the FormatURL offset adjustments. size_t offset = GetText().find(original_text); if (offset != base::string16::npos) { + AutocompleteMatch match; + model()->ClassifyString(original_selected_text, &match, nullptr); + bool selection_classifes_as_search = + AutocompleteMatch::IsSearchType(match.type); if (start != end && gesture == UnelisionGesture::MOUSE_RELEASE && - !model()->ClassifiesAsSearch(original_selected_text)) { + !selection_classifes_as_search) { // For user selections that look like a URL instead of a Search: // If we are uneliding at the end of a drag-select (on mouse release), // and the selection spans to the beginning of the elided URL, ensure that @@ -1010,7 +1014,9 @@ base::string16 selection_text = gfx::TruncateString( clipboard_text, kMaxSelectionTextLength, gfx::WORD_BREAK); - if (model()->ClassifiesAsSearch(clipboard_text)) + AutocompleteMatch match; + model()->ClassifyString(clipboard_text, &match, nullptr); + if (AutocompleteMatch::IsSearchType(match.type)) return l10n_util::GetStringFUTF16(IDS_PASTE_AND_SEARCH, selection_text); // To ensure the search and url strings began to truncate at the exact same @@ -1020,7 +1026,7 @@ const float kMaxSelectionPixelWidth = GetStringWidthF( selection_text, Textfield::GetFontList(), gfx::Typesetter::BROWSER); base::string16 url = url_formatter::ElideUrl( - GURL(clipboard_text), Textfield::GetFontList(), kMaxSelectionPixelWidth, + match.destination_url, Textfield::GetFontList(), kMaxSelectionPixelWidth, gfx::Typesetter::BROWSER); return l10n_util::GetStringFUTF16(IDS_PASTE_AND_GO, url);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc index a94c7cbe..c14ec2c3 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -585,8 +585,7 @@ clipboard->Clear(clipboard_type); EXPECT_FALSE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO)); - // Test command is enabled and the correct label text is returned with a URL - // on the clipboard. + // Test input that's a valid URL. base::string16 expected_text = #if defined(OS_MACOSX) base::ASCIIToUTF16("Pa&ste and Go to https://test.com"); @@ -600,8 +599,20 @@ EXPECT_TRUE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO)); EXPECT_EQ(expected_text, returned_text); - // Test command is enabled and the correct label text is returned with a - // search on the clipboard. + // Test input that's URL-like. (crbug.com/980002). + expected_text = +#if defined(OS_MACOSX) + base::ASCIIToUTF16("Pa&ste and Go to test.com"); +#else + base::ASCIIToUTF16("Pa&ste and go to test.com"); +#endif + ui::ScopedClipboardWriter(clipboard_type) + .WriteText(base::ASCIIToUTF16("test.com")); + returned_text = omnibox_view()->GetLabelForCommandId(IDC_PASTE_AND_GO); + EXPECT_TRUE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO)); + EXPECT_EQ(expected_text, returned_text); + + // Test input that's search-like. expected_text = #if defined(OS_MACOSX) base::WideToUTF16(
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc index c8f9491..07a76c2 100644 --- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc +++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc
@@ -122,7 +122,9 @@ TryChromeDialog::OPEN_CHROME_DEFAULT}, {IDS_WIN10_TOAST_RECOMMENDATION, 0, ExperimentVariations::CloseStyle::kCloseX, - TryChromeDialog::OPEN_CHROME_WELCOME_WIN10}, + // Was formerly OPEN_CHROME_WELCOME_WIN10 but that UI is deprecated, + // so now kExperiments[3] == kExperiments[4]. + TryChromeDialog::OPEN_CHROME_WELCOME}, {IDS_WIN10_TOAST_RECOMMENDATION, 0, ExperimentVariations::CloseStyle::kCloseX, TryChromeDialog::OPEN_CHROME_WELCOME},
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h index 8728443..3a38122 100644 --- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h +++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h
@@ -52,7 +52,6 @@ enum Result { NOT_NOW, // Don't launch chrome. Exit now. OPEN_CHROME_WELCOME, // Launch Chrome to the standard Welcome page. - OPEN_CHROME_WELCOME_WIN10, // Launch Chrome to the Win10 Welcome page. OPEN_CHROME_DEFAULT, // Launch Chrome to the default page. OPEN_CHROME_DEFER, // Launch Chrome on account of a rendezvous, // deferring to the caller's command line.
diff --git a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc index 97715ac..a7a7799 100644 --- a/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc +++ b/chrome/chrome_cleaner/engines/broker/scanner_sandbox_interface.cc
@@ -30,12 +30,6 @@ #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/win_utils.h" -// Redefine NT_ERROR and STATUS_INVALID_INFO_CLASS here. Can't use the system -// header <ntdef.h> as it conflicts with sandbox/win/src/nt_internals.h. -#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3) - -#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003L) - namespace chrome_cleaner_sandbox { namespace { @@ -43,32 +37,6 @@ using chrome_cleaner::SandboxErrorCode; using KnownFolder = chrome_cleaner::mojom::KnownFolder; -// For the structure documentation, see -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx -// Can't use the system header <Winternl.h> as it conflicts with -// sandbox/win/src/nt_internals.h. -typedef struct _RTL_USER_PROCESS_PARAMETERS { - BYTE Reserved1[16]; - PVOID Reserved2[10]; - UNICODE_STRING ImagePathName; - UNICODE_STRING CommandLine; -} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; - -// Partial definition from -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx -// Cannot use the definition from sandbox/win/src/nt_internals.h as it doesn't -// contain ProcessParameters field. -// TODO(veranika): Fix the definition from nt_internals.h and use it instead of -// this custom one. -typedef struct _PEB { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PVOID Ldr; - PRTL_USER_PROCESS_PARAMETERS ProcessParameters; -} PEB, *PPEB; - bool SandboxKnownFolderIdToPathServiceKey(KnownFolder folder_id, int* path_service_key) { switch (folder_id) {
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index eb3a8bb..dd4cc1f0 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -2707,4 +2707,9 @@ const char kSharingSyncedDevices[] = "sharing.synced_devices"; const char kSharingFCMRegistration[] = "sharing.fcm_registration"; +#if !defined(OS_ANDROID) +// Dictionary that contains all of the Hats Survey Metadata. +const char kHatsSurveyMetadata[] = "hats.survey_metadata"; +#endif // !defined(OS_ANDROID) + } // namespace prefs
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 0d62bfc1..be7a19a 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -957,6 +957,10 @@ extern const char kSharingSyncedDevices[]; extern const char kSharingFCMRegistration[]; +#if !defined(OS_ANDROID) +extern const char kHatsSurveyMetadata[]; +#endif // !defined(OS_ANDROID) + } // namespace prefs #endif // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn index c4a617d0..1425094 100644 --- a/chrome/installer/mac/BUILD.gn +++ b/chrome/installer/mac/BUILD.gn
@@ -101,6 +101,7 @@ "$root_out_dir/liblzma_decompress.dylib", "$root_out_dir/xz", "$root_out_dir/xzdec", + "//chrome/app/helper-gpu-entitlements.plist", "//chrome/app/helper-plugin-entitlements.plist", "//chrome/app/helper-renderer-entitlements.plist", "dirdiffer.sh",
diff --git a/chrome/installer/mac/signing/modification_test.py b/chrome/installer/mac/signing/modification_test.py index 6e546d93b..e612ac7 100644 --- a/chrome/installer/mac/signing/modification_test.py +++ b/chrome/installer/mac/signing/modification_test.py
@@ -27,6 +27,7 @@ 'com.apple.application-identifier': bundle_id }, '$W/helper-renderer-entitlements.plist': {}, + '$W/helper-gpu-entitlements.plist': {}, '$W/helper-plugin-entitlements.plist': {}, '$W/App Product Canary.app/Contents/Resources/test.signing.bundle_id.canary.manifest/Contents/Resources/test.signing.bundle_id.canary.manifest': { @@ -94,10 +95,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -124,10 +127,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -154,10 +159,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -184,10 +191,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -214,10 +223,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -245,10 +256,12 @@ '$W/App Product.app/Contents/Info.plist', ) - self.assertEqual(3, kwargs['copy_files'].call_count) + self.assertEqual(4, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -284,10 +297,12 @@ ), ]) - self.assertEqual(5, kwargs['copy_files'].call_count) + self.assertEqual(6, kwargs['copy_files'].call_count) kwargs['copy_files'].assert_has_calls([ mock.call('$I/Product Packaging/app-entitlements.plist', '$W/app-entitlements.plist'), + mock.call('$I/Product Packaging/helper-gpu-entitlements.plist', + '$W/helper-gpu-entitlements.plist'), mock.call('$I/Product Packaging/helper-renderer-entitlements.plist', '$W/helper-renderer-entitlements.plist'), mock.call('$I/Product Packaging/helper-plugin-entitlements.plist', @@ -301,7 +316,7 @@ kwargs['write_file'].assert_called_once_with( '$W/App Product Canary.app/Contents/PkgInfo', 'APPLMooo') - self.assertEqual(6, plistlib.writePlist.call_count) + self.assertEqual(7, plistlib.writePlist.call_count) plistlib.writePlist.assert_has_calls([ mock.call({ 'CFBundleIdentifier': @@ -321,6 +336,7 @@ 'com.apple.application-identifier': 'test.signing.bundle_id.canary' }, '$W/app-entitlements.plist'), + mock.call({}, '$W/helper-gpu-entitlements.plist'), mock.call({}, '$W/helper-renderer-entitlements.plist'), mock.call({}, '$W/helper-plugin-entitlements.plist'), mock.call({
diff --git a/chrome/installer/mac/signing/pipeline_test.py b/chrome/installer/mac/signing/pipeline_test.py index 5cfd0c7..34b322d 100644 --- a/chrome/installer/mac/signing/pipeline_test.py +++ b/chrome/installer/mac/signing/pipeline_test.py
@@ -208,6 +208,9 @@ mock.call( '$W/App Product.app/Contents/Frameworks/Product Framework.framework/Helpers/Product Helper (Plugin).app' ), + mock.call( + '$W/App Product.app/Contents/Frameworks/Product Framework.framework/Helpers/Product Helper (GPU).app' + ), mock.call('$W/App Product.app') ]) @@ -231,11 +234,12 @@ mock.call( '$W/App Product Canary.app/Contents/Frameworks/Product Framework.framework/Helpers/Product Helper (Plugin).app' ), + mock.call( + '$W/App Product Canary.app/Contents/Frameworks/Product Framework.framework/Helpers/Product Helper (GPU).app' + ), mock.call('$W/App Product Canary.app') ]) - print(staple.mock_calls) - def test_package_and_sign_no_branding(self, **kwargs): manager = mock.Mock() for attr in kwargs:
diff --git a/chrome/installer/mac/signing/signing.py b/chrome/installer/mac/signing/signing.py index de69f7ba..9a425e7 100644 --- a/chrome/installer/mac/signing/signing.py +++ b/chrome/installer/mac/signing/signing.py
@@ -89,6 +89,18 @@ CodeSignOptions.HARDENED_RUNTIME, entitlements='helper-renderer-entitlements.plist', verify_options=VerifyOptions.DEEP), + 'helper-gpu-app': + CodeSignedProduct( + '{0.framework_dir}/Helpers/{0.product} Helper (GPU).app' + .format(config), + '{}.helper.gpu'.format(uncustomized_bundle_id), + # Do not use |full_hardened_runtime_options| because library + # validation is incompatible with more permissive code signing + # entitlements. + options=CodeSignOptions.RESTRICT + CodeSignOptions.KILL + + CodeSignOptions.HARDENED_RUNTIME, + entitlements='helper-gpu-entitlements.plist', + verify_options=VerifyOptions.DEEP), 'helper-plugin-app': CodeSignedProduct( '{0.framework_dir}/Helpers/{0.product} Helper (Plugin).app'
diff --git a/chrome/installer/mac/signing/signing_test.py b/chrome/installer/mac/signing/signing_test.py index 8555d3d..72a0f01 100644 --- a/chrome/installer/mac/signing/signing_test.py +++ b/chrome/installer/mac/signing/signing_test.py
@@ -78,6 +78,10 @@ self.assertEqual( set(model.CodeSignOptions.RESTRICT + model.CodeSignOptions.KILL + model.CodeSignOptions.HARDENED_RUNTIME), + set(parts['helper-gpu-app'].options)) + self.assertEqual( + set(model.CodeSignOptions.RESTRICT + model.CodeSignOptions.KILL + + model.CodeSignOptions.HARDENED_RUNTIME), set(parts['helper-plugin-app'].options)) self.assertEqual( set(model.CodeSignOptions.RESTRICT +
diff --git a/chrome/renderer/prerender/OWNERS b/chrome/renderer/prerender/OWNERS index b10effc74..c71af0e 100644 --- a/chrome/renderer/prerender/OWNERS +++ b/chrome/renderer/prerender/OWNERS
@@ -1,5 +1,3 @@ -mmenke@chromium.org -pasko@chromium.org -tbansal@chromium.org +file://chrome/browser/prerender/OWNERS # COMPONENT: Internals>Preload
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index 0e4a9de..c5e3aa72 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -12,6 +12,7 @@ #include "base/i18n/rtl.h" #include "base/json/string_escape.h" #include "base/macros.h" +#include "base/metrics/user_metrics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -32,6 +33,7 @@ #include "content/public/common/page_zoom.h" #include "content/public/renderer/chrome_object_extensions_utils.h" #include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "gin/data_object_builder.h" #include "gin/handle.h" @@ -1215,6 +1217,8 @@ if (!search_box) return; search_box->ApplyDefaultTheme(); + content::RenderThread::Get()->RecordAction( + base::UserMetricsAction("ChromeColors_DefaultApplied")); } // static @@ -1224,6 +1228,8 @@ return; search_box->ApplyDefaultTheme(); search_box->ConfirmThemeChanges(); + content::RenderThread::Get()->RecordAction( + base::UserMetricsAction("ChromeColors_ThemeUninstalled")); } // static @@ -1233,8 +1239,11 @@ if (!search_box || !value->IsArray()) return; SkColor color; - if (ArrayToSkColor(isolate, value.As<v8::Array>(), &color)) + if (ArrayToSkColor(isolate, value.As<v8::Array>(), &color)) { search_box->ApplyAutogeneratedTheme(color); + content::RenderThread::Get()->RecordAction( + base::UserMetricsAction("ChromeColors_ColorApplied")); + } } // static @@ -1243,6 +1252,8 @@ if (!search_box) return; search_box->RevertThemeChanges(); + content::RenderThread::Get()->RecordAction( + base::UserMetricsAction("ChromeColors_MenuCancel")); } // static @@ -1251,6 +1262,8 @@ if (!search_box) return; search_box->ConfirmThemeChanges(); + content::RenderThread::Get()->RecordAction( + base::UserMetricsAction("ChromeColors_MenuDone")); } v8::Local<v8::Value> NewTabPageBindings::GetColorsInfo(v8::Isolate* isolate) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index c933ae0c..8b95111 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3643,6 +3643,7 @@ "../browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc", "../browser/safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win_unittest.cc", "../browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc", + "../browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc", "../browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc", "../browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h", "../browser/safe_browsing/chrome_cleaner/srt_delete_extension_win_unittest.cc", @@ -5029,6 +5030,11 @@ if (enable_openscreen) { include_dirs = [ "//third_party/openscreen/src" ] + deps += [ + "//third_party/openscreen/src/platform:platform_unittests", + "//third_party/openscreen/src/platform:test", + ] + sources += [ "../browser/media/router/providers/openscreen/discovery/open_screen_listener_unittest.cc", "../browser/media/router/providers/openscreen/network_service_quic_packet_writer_unittest.cc", @@ -5301,6 +5307,10 @@ "ppapi/ppapi_interactive_browsertest.cc", ] + if (is_win && use_aura) { + sources += [ "../browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc" ] + } + configs += [ "//build/config:precompiled_headers" ] if (is_linux && !is_component_build) { configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
diff --git a/chrome/test/data/accessibility/input_datalist.html b/chrome/test/data/accessibility/input_datalist.html new file mode 100644 index 0000000..a5e8711d --- /dev/null +++ b/chrome/test/data/accessibility/input_datalist.html
@@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<body> + <input aria-label="input" id="datalist" list="autosuggest" aria-controls="checkbox"> + <datalist id="autosuggest"> + <option value="option1"> + <option value="option2"> + <option value="option3"> + </datalist> + + <div role="checkbox" id="checkbox">checkbox</div> +</body>
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1 new file mode 100644 index 0000000..978791a --- /dev/null +++ b/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_folder_selected.Nexus_5-19.png.sha1
@@ -0,0 +1 @@ +a7b6d7735bc4e0e2646f098433cdb2083e85ffc6 \ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1 new file mode 100644 index 0000000..d35f3d8 --- /dev/null +++ b/chrome/test/data/android/render_tests/BookmarkReorderTest.bookmark_manager_one_folder.Nexus_5-19.png.sha1
@@ -0,0 +1 @@ +498c2ad61394be8b7bdf4e003e7dc995cf789a7c \ No newline at end of file
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 9e88005..6362de2 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -394,13 +394,7 @@ }, "ExternalPrintServers": { - "os": ["chromeos"], - "test_policy": { - "ExternalPrintServers": { - "url": "https://example.com/policyfile", - "hash": "deadbeefdeadbeefdeadbeef" - } - } + "note": "This policy will be added in R-78" }, "NativePrinters": {
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn index a94a156..890fb65c 100644 --- a/chromecast/browser/BUILD.gn +++ b/chromecast/browser/BUILD.gn
@@ -68,8 +68,6 @@ "cast_media_blocker.h", "cast_navigation_ui_data.cc", "cast_navigation_ui_data.h", - "cast_net_log.cc", - "cast_net_log.h", "cast_network_contexts.cc", "cast_network_contexts.h", "cast_network_delegate.cc",
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn index e730107..699dabe8 100644 --- a/chromecast/browser/android/BUILD.gn +++ b/chromecast/browser/android/BUILD.gn
@@ -55,7 +55,16 @@ android_resources("cast_shell_android_resources") { android_manifest = cast_shell_android_manifest android_manifest_dep = ":cast_shell_manifest" - resource_dirs = [ "//chromecast/browser/android/apk/res" ] + resource_dirs = [ + "//chromecast/browser/android/apk/res", + "//chromecast/browser/android/apk/res-values", + ] +} + +android_resources("cast_shell_android_stub_resources") { + android_manifest = cast_shell_android_manifest + android_manifest_dep = ":cast_shell_manifest" + resource_dirs = [ "//chromecast/browser/android/apk/res-values" ] } android_library("cast_audio_manager_java") {
diff --git a/chromecast/browser/android/apk/res/values-v17/styles.xml b/chromecast/browser/android/apk/res-values/values-v17/styles.xml similarity index 77% rename from chromecast/browser/android/apk/res/values-v17/styles.xml rename to chromecast/browser/android/apk/res-values/values-v17/styles.xml index 80eaf9b..d652a61 100644 --- a/chromecast/browser/android/apk/res/values-v17/styles.xml +++ b/chromecast/browser/android/apk/res-values/values-v17/styles.xml
@@ -5,8 +5,8 @@ found in the LICENSE file. --> -<resources> - <style name="CastShellTheme" parent="@android:style/Theme.Holo.Light.NoActionBar"> +<resources xmlns:tools="http://schemas.android.com/tools"> + <style name="CastShellTheme" parent="@android:style/Theme.Holo.Light.NoActionBar" tools:ignore="UnusedResources"> <item name="android:windowBackground">@android:color/black</item> </style> </resources>
diff --git a/chromecast/browser/android/apk/res/values/strings.xml b/chromecast/browser/android/apk/res-values/values/strings.xml similarity index 77% rename from chromecast/browser/android/apk/res/values/strings.xml rename to chromecast/browser/android/apk/res-values/values/strings.xml index 9ca510a6..6e01b910 100644 --- a/chromecast/browser/android/apk/res/values/strings.xml +++ b/chromecast/browser/android/apk/res-values/values/strings.xml
@@ -5,7 +5,9 @@ found in the LICENSE file. --> -<resources xmlns:android="http://schemas.android.com/apk/res/android"> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="MissingTranslation,UnusedResources"> <string name="app_name">Chromecast Android Shell</string> <string name="browser_process_initialization_failed">Initialization failed.</string> </resources>
diff --git a/chromecast/browser/android/apk/res/layout/cast_web_contents_activity.xml b/chromecast/browser/android/apk/res/layout/cast_web_contents_activity.xml index 4e42fda..deaeb89 100644 --- a/chromecast/browser/android/apk/res/layout/cast_web_contents_activity.xml +++ b/chromecast/browser/android/apk/res/layout/cast_web_contents_activity.xml
@@ -5,11 +5,14 @@ found in the LICENSE file. --> +<!-- TODO(ziyangch): Declare web_contents_container as the layout for CastWebContentsActivity/Fragment in the AndroidManifest --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:id="@+id/web_contents_container" android:background="#FFFFFF" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="1"> + android:layout_weight="1" + tools:ignore="UnusedResources"> </FrameLayout>
diff --git a/chromecast/browser/cast_browser_context.cc b/chromecast/browser/cast_browser_context.cc index 55ab2b4..192f835f 100644 --- a/chromecast/browser/cast_browser_context.cc +++ b/chromecast/browser/cast_browser_context.cc
@@ -9,7 +9,6 @@ #include "base/barrier_closure.h" #include "base/command_line.h" -#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/macros.h" #include "base/path_service.h" @@ -25,9 +24,6 @@ #include "content/public/browser/shared_cors_origin_access_list.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" -#include "services/network/public/cpp/features.h" namespace chromecast { namespace shell { @@ -48,10 +44,8 @@ DISALLOW_COPY_AND_ASSIGN(CastResourceContext); }; -CastBrowserContext::CastBrowserContext( - URLRequestContextFactory* url_request_context_factory) - : url_request_context_factory_(url_request_context_factory), - resource_context_(new CastResourceContext), +CastBrowserContext::CastBrowserContext() + : resource_context_(new CastResourceContext), shared_cors_origin_access_list_( content::SharedCorsOriginAccessList::Create()) { InitWhileIOAllowed(); @@ -101,10 +95,6 @@ return false; } -net::URLRequestContextGetter* CastBrowserContext::GetSystemRequestContext() { - return url_request_context_factory_->GetSystemGetter(); -} - content::ResourceContext* CastBrowserContext::GetResourceContext() { return resource_context_.get(); } @@ -165,12 +155,13 @@ net::URLRequestContextGetter* CastBrowserContext::CreateRequestContext( content::ProtocolHandlerMap* protocol_handlers, content::URLRequestInterceptorScopedVector request_interceptors) { - return url_request_context_factory_->CreateMainGetter( - this, protocol_handlers, std::move(request_interceptors)); + NOTREACHED(); + return nullptr; } net::URLRequestContextGetter* CastBrowserContext::CreateMediaRequestContext() { - return url_request_context_factory_->GetMediaGetter(); + NOTREACHED(); + return nullptr; } void CastBrowserContext::SetCorsOriginAccessListForOrigin( @@ -178,29 +169,22 @@ std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns, std::vector<network::mojom::CorsOriginPatternPtr> block_patterns, base::OnceClosure closure) { - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { - shared_cors_origin_access_list_->SetForOrigin( - source_origin, std::move(allow_patterns), std::move(block_patterns), - std::move(closure)); - } else { - auto barrier_closure = BarrierClosure(2, std::move(closure)); + auto barrier_closure = BarrierClosure(2, std::move(closure)); - // Keep profile storage partitions' NetworkContexts synchronized. - auto profile_setter = base::MakeRefCounted<CorsOriginPatternSetter>( - source_origin, CorsOriginPatternSetter::ClonePatterns(allow_patterns), - CorsOriginPatternSetter::ClonePatterns(block_patterns), - barrier_closure); - ForEachStoragePartition( - this, base::BindRepeating(&CorsOriginPatternSetter::SetLists, - base::RetainedRef(profile_setter.get()))); + // Keep profile storage partitions' NetworkContexts synchronized. + auto profile_setter = base::MakeRefCounted<CorsOriginPatternSetter>( + source_origin, CorsOriginPatternSetter::ClonePatterns(allow_patterns), + CorsOriginPatternSetter::ClonePatterns(block_patterns), barrier_closure); + ForEachStoragePartition( + this, base::BindRepeating(&CorsOriginPatternSetter::SetLists, + base::RetainedRef(profile_setter.get()))); - // Keep the per-profile access list up to date so that we can use this to - // restore NetworkContext settings at anytime, e.g. on restarting the - // network service. - shared_cors_origin_access_list_->SetForOrigin( - source_origin, std::move(allow_patterns), std::move(block_patterns), - barrier_closure); - } + // Keep the per-profile access list up to date so that we can use this to + // restore NetworkContext settings at anytime, e.g. on restarting the + // network service. + shared_cors_origin_access_list_->SetForOrigin( + source_origin, std::move(allow_patterns), std::move(block_patterns), + barrier_closure); } content::SharedCorsOriginAccessList*
diff --git a/chromecast/browser/cast_browser_context.h b/chromecast/browser/cast_browser_context.h index d2e3e31..c20a9d9 100644 --- a/chromecast/browser/cast_browser_context.h +++ b/chromecast/browser/cast_browser_context.h
@@ -16,15 +16,12 @@ namespace chromecast { namespace shell { -class URLRequestContextFactory; - // Chromecast does not currently support multiple profiles. So there is a // single BrowserContext for all chromecast renderers. // There is no support for PartitionStorage. class CastBrowserContext final : public content::BrowserContext { public: - explicit CastBrowserContext( - URLRequestContextFactory* url_request_context_factory); + CastBrowserContext(); ~CastBrowserContext() override; // BrowserContext implementation: @@ -53,8 +50,6 @@ content::URLRequestInterceptorScopedVector request_interceptors) override; net::URLRequestContextGetter* CreateMediaRequestContext() override; - net::URLRequestContextGetter* GetSystemRequestContext(); - void SetCorsOriginAccessListForOrigin( const url::Origin& source_origin, std::vector<network::mojom::CorsOriginPatternPtr> allow_patterns, @@ -69,7 +64,6 @@ // allowed on the current thread. void InitWhileIOAllowed(); - URLRequestContextFactory* const url_request_context_factory_; base::FilePath path_; std::unique_ptr<CastResourceContext> resource_context_; std::unique_ptr<content::PermissionControllerDelegate> permission_manager_;
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc index 65a44b3..5d750fe 100644 --- a/chromecast/browser/cast_browser_main_parts.cc +++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -37,7 +37,6 @@ #include "chromecast/browser/cast_content_browser_client.h" #include "chromecast/browser/cast_feature_list_creator.h" #include "chromecast/browser/cast_memory_pressure_monitor.h" -#include "chromecast/browser/cast_net_log.h" #include "chromecast/browser/devtools/remote_debugging_server.h" #include "chromecast/browser/media/media_caps_impl.h" #include "chromecast/browser/metrics/cast_browser_metrics.h" @@ -67,7 +66,6 @@ #include "gpu/command_buffer/service/gpu_switches.h" #include "media/base/media.h" #include "media/base/media_switches.h" -#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "ui/base/ui_base_switches.h" #include "ui/gl/gl_switches.h" @@ -359,9 +357,6 @@ parameters_(parameters), cast_content_browser_client_(cast_content_browser_client), url_request_context_factory_(url_request_context_factory), - net_log_(!base::FeatureList::IsEnabled(network::features::kNetworkService) - ? new CastNetLog() - : nullptr), media_caps_(new media::MediaCapsImpl()) { DCHECK(cast_content_browser_client); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -509,8 +504,7 @@ base::Unretained(this))); #endif // defined(OS_ANDROID) - cast_browser_process_->SetNetLog(net_log_.get()); - url_request_context_factory_->InitializeOnUIThread(net_log_.get()); + url_request_context_factory_->InitializeOnUIThread(nullptr); cast_browser_process_->SetConnectivityChecker(ConnectivityChecker::Create( base::CreateSingleThreadTaskRunnerWithTraits( @@ -518,7 +512,7 @@ url_request_context_factory_->GetSystemGetter())); cast_browser_process_->SetBrowserContext( - std::make_unique<CastBrowserContext>(url_request_context_factory_)); + std::make_unique<CastBrowserContext>()); cast_browser_process_->SetMetricsServiceClient( std::make_unique<metrics::CastMetricsServiceClient>( cast_browser_process_->browser_client(), @@ -564,7 +558,6 @@ cast_browser_process_->browser_client()->CreateCastService( cast_browser_process_->browser_context(), cast_browser_process_->pref_service(), - url_request_context_factory_->GetSystemGetter(), video_plane_controller_.get(), window_manager_.get())); cast_browser_process_->cast_service()->Initialize();
diff --git a/chromecast/browser/cast_browser_main_parts.h b/chromecast/browser/cast_browser_main_parts.h index 7364022..c20516f 100644 --- a/chromecast/browser/cast_browser_main_parts.h +++ b/chromecast/browser/cast_browser_main_parts.h
@@ -27,10 +27,6 @@ class ExtensionsBrowserClient; } // namespace extensions -namespace net { -class NetLog; -} - #if defined(USE_AURA) namespace views { class ViewsDelegate; @@ -94,7 +90,6 @@ // Caches a pointer of the CastContentBrowserClient. CastContentBrowserClient* const cast_content_browser_client_ = nullptr; URLRequestContextFactory* const url_request_context_factory_; - std::unique_ptr<net::NetLog> net_log_; std::unique_ptr<media::VideoPlaneController> video_plane_controller_; std::unique_ptr<media::MediaCapsImpl> media_caps_;
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index b610437..9db1a07e 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc
@@ -79,7 +79,6 @@ #include "media/mojo/buildflags.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/url_request/url_request_context_getter.h" -#include "services/network/public/cpp/features.h" #include "services/service_manager/embedder/descriptors.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -216,10 +215,9 @@ CastContentBrowserClient::CastContentBrowserClient( CastFeatureListCreator* cast_feature_list_creator) : cast_browser_main_parts_(nullptr), + cast_network_contexts_(std::make_unique<CastNetworkContexts>()), url_request_context_factory_(new URLRequestContextFactory()), cast_feature_list_creator_(cast_feature_list_creator) { - cast_network_contexts_ = - std::make_unique<CastNetworkContexts>(url_request_context_factory_.get()); cast_feature_list_creator_->SetExtraEnableFeatures({ ::media::kInternalMediaSession, features::kNetworkServiceInProcess, @@ -250,7 +248,6 @@ std::unique_ptr<CastService> CastContentBrowserClient::CreateCastService( content::BrowserContext* browser_context, PrefService* pref_service, - net::URLRequestContextGetter* request_context_getter, media::VideoPlaneController* video_plane_controller, CastWindowManager* window_manager) { return std::make_unique<CastServiceSimple>(browser_context, pref_service, @@ -1043,9 +1040,6 @@ void CastContentBrowserClient::OnNetworkServiceCreated( network::mojom::NetworkService* network_service) { - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) - return; - // Need to set up global NetworkService state before anything else uses it. cast_network_contexts_->OnNetworkServiceCreated(network_service); } @@ -1055,11 +1049,6 @@ content::BrowserContext* context, bool in_memory, const base::FilePath& relative_partition_path) { - // StoragePartition will wrap the URLRequestContext it owns with a - // NetworkContext pipe if network service is disabled. - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) - return nullptr; - return cast_network_contexts_->CreateNetworkContext(context, in_memory, relative_partition_path); }
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h index 623ffc0..34732279 100644 --- a/chromecast/browser/cast_content_browser_client.h +++ b/chromecast/browser/cast_content_browser_client.h
@@ -42,7 +42,6 @@ namespace net { class SSLPrivateKey; -class URLRequestContextGetter; class X509Certificate; } @@ -81,12 +80,9 @@ ~CastContentBrowserClient() override; // Creates and returns the CastService instance for the current process. - // Note: |request_context_getter| might be different than the main request - // getter accessible via CastBrowserProcess. virtual std::unique_ptr<CastService> CreateCastService( content::BrowserContext* browser_context, PrefService* pref_service, - net::URLRequestContextGetter* request_context_getter, media::VideoPlaneController* video_plane_controller, CastWindowManager* window_manager);
diff --git a/chromecast/browser/cast_net_log.cc b/chromecast/browser/cast_net_log.cc deleted file mode 100644 index 4a0c9e6..0000000 --- a/chromecast/browser/cast_net_log.cc +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromecast/browser/cast_net_log.h" - -#include <stdio.h> - -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/files/scoped_file.h" -#include "base/values.h" -#include "content/public/common/content_switches.h" -#include "net/log/file_net_log_observer.h" -#include "net/log/net_log_util.h" -#include "services/network/public/cpp/network_switches.h" - -namespace chromecast { - -namespace { - -std::unique_ptr<base::DictionaryValue> GetShellConstants() { - std::unique_ptr<base::DictionaryValue> constants_dict = - net::GetNetConstants(); - - // Add a dictionary with client information - auto dict = std::make_unique<base::DictionaryValue>(); - - dict->SetString("name", "cast_shell"); - dict->SetString( - "command_line", - base::CommandLine::ForCurrentProcess()->GetCommandLineString()); - - constants_dict->Set("clientInfo", std::move(dict)); - - return constants_dict; -} - -} // namespace - -CastNetLog::CastNetLog() { - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - - if (command_line->HasSwitch(network::switches::kLogNetLog)) { - base::FilePath log_path = - command_line->GetSwitchValuePath(network::switches::kLogNetLog); - net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::kDefault; - - file_net_log_observer_ = - net::FileNetLogObserver::CreateUnbounded(log_path, GetShellConstants()); - - file_net_log_observer_->StartObserving(this, capture_mode); - } -} - -CastNetLog::~CastNetLog() { - // Remove the observer we own before we're destroyed. - if (file_net_log_observer_) - file_net_log_observer_->StopObserving(nullptr, base::OnceClosure()); -} - -} // namespace chromecast
diff --git a/chromecast/browser/cast_net_log.h b/chromecast/browser/cast_net_log.h deleted file mode 100644 index a6deab0..0000000 --- a/chromecast/browser/cast_net_log.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMECAST_BROWSER_CAST_NET_LOG_H_ -#define CHROMECAST_BROWSER_CAST_NET_LOG_H_ - -#include <memory> -#include <string> - -#include "base/macros.h" -#include "net/log/net_log.h" - -namespace net { -class FileNetLogObserver; -} - -namespace chromecast { - -// TODO(http://crbug.com/904910): Delete this once full switched over to Network -// Service. -class CastNetLog : public net::NetLog { - public: - CastNetLog(); - ~CastNetLog() override; - - private: - std::unique_ptr<net::FileNetLogObserver> file_net_log_observer_; - - DISALLOW_COPY_AND_ASSIGN(CastNetLog); -}; - -} // namespace chromecast - -#endif // CHROMECAST_BROWSER_CAST_NET_LOG_H_
diff --git a/chromecast/browser/cast_network_contexts.cc b/chromecast/browser/cast_network_contexts.cc index 64d14d4b..9475983 100644 --- a/chromecast/browser/cast_network_contexts.cc +++ b/chromecast/browser/cast_network_contexts.cc
@@ -25,7 +25,6 @@ #include "content/public/browser/storage_partition.h" #include "services/network/network_context.h" #include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h" -#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" namespace chromecast { @@ -83,53 +82,9 @@ DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryForSystem); }; -// Class to own the NetworkContext wrapping the system URLRequestContext when -// the network service is disabled. -// -// Created on the UI thread, but must be initialized and destroyed on the IO -// thread. -class CastNetworkContexts::SystemNetworkContextOwner { - public: - SystemNetworkContextOwner() { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - } - - ~SystemNetworkContextOwner() { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - } - - void Initialize(network::mojom::NetworkContextRequest network_context_request, - scoped_refptr<net::URLRequestContextGetter> context_getter) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - context_getter_ = std::move(context_getter); - network::mojom::NetworkContextParamsPtr network_context_params = - network::mojom::NetworkContextParams::New(); - content::UpdateCorsExemptHeader(network_context_params.get()); - variations::UpdateCorsExemptHeaderForVariations( - network_context_params.get()); - network_context_ = std::make_unique<network::NetworkContext>( - content::GetNetworkServiceImpl(), std::move(network_context_request), - context_getter_->GetURLRequestContext(), - network_context_params->cors_exempt_header_list); - } - - private: - // Reference to the URLRequestContextGetter for the URLRequestContext used by - // NetworkContext. Depending on the embedder's implementation, this may be - // needed to keep the URLRequestContext alive until the NetworkContext is - // destroyed. - scoped_refptr<net::URLRequestContextGetter> context_getter_; - std::unique_ptr<network::mojom::NetworkContext> network_context_; - - DISALLOW_COPY_AND_ASSIGN(SystemNetworkContextOwner); -}; - -CastNetworkContexts::CastNetworkContexts( - URLRequestContextFactory* url_request_context_factory) - : url_request_context_factory_(url_request_context_factory) { - system_shared_url_loader_factory_ = - base::MakeRefCounted<URLLoaderFactoryForSystem>(this); -} +CastNetworkContexts::CastNetworkContexts() + : system_shared_url_loader_factory_( + base::MakeRefCounted<URLLoaderFactoryForSystem>(this)) {} CastNetworkContexts::~CastNetworkContexts() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -140,20 +95,6 @@ network::mojom::NetworkContext* CastNetworkContexts::GetSystemContext() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { - if (!system_network_context_) { - system_network_context_owner_.reset(new SystemNetworkContextOwner); - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&SystemNetworkContextOwner::Initialize, - base::Unretained(system_network_context_owner_.get()), - MakeRequest(&system_network_context_), - base::WrapRefCounted( - url_request_context_factory_->GetSystemGetter()))); - } - return system_network_context_.get(); - } - if (!system_network_context_ || system_network_context_.encountered_error()) { // This should call into OnNetworkServiceCreated(), which will re-create // the network service, if needed. There's a chance that it won't be @@ -199,7 +140,6 @@ bool in_memory, const base::FilePath& relative_partition_path) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); network::mojom::NetworkContextPtr network_context; network::mojom::NetworkContextParamsPtr context_params = @@ -217,9 +157,6 @@ void CastNetworkContexts::OnNetworkServiceCreated( network::mojom::NetworkService* network_service) { - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) - return; - // Disable QUIC if instructed by DCS. This remains constant for the lifetime // of the process. if (!chromecast::IsFeatureEnabled(kEnableQuic)) @@ -233,9 +170,6 @@ void CastNetworkContexts::OnLocaleUpdate() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) - return; - auto accept_language = CastHttpUserAgentSettings::AcceptLanguage(); GetSystemContext()->SetAcceptLanguage(accept_language);
diff --git a/chromecast/browser/cast_network_contexts.h b/chromecast/browser/cast_network_contexts.h index f993cea9..1c86cfab 100644 --- a/chromecast/browser/cast_network_contexts.h +++ b/chromecast/browser/cast_network_contexts.h
@@ -35,21 +35,16 @@ namespace chromecast { namespace shell { -class URLRequestContextFactory; // This class owns the NetworkContext used for the system and for configuring it // along with the BrowserContext's NetworkContext. -// If the network service is disabled, it will create a NetworkContext object -// that wraps the system URLRequestContext from URLRequestContextFactory. -// Otherwise it will create and configure its own NetworkContext for the system, -// and create the BrowserContext's main StoragePartition's NetworkContext. +// It will create and configure its own NetworkContext for the system, and +// create the BrowserContext's main StoragePartition's NetworkContext. // It lives on the UI thread. class CastNetworkContexts : public net::ProxyConfigService::Observer, public network::mojom::ProxyConfigPollerClient { public: - // |url_request_context_factory| needs to outlive this object. - explicit CastNetworkContexts( - URLRequestContextFactory* url_request_context_factory); + CastNetworkContexts(); ~CastNetworkContexts() override; // Returns the System NetworkContext. Does any initialization of the @@ -84,7 +79,6 @@ void OnPrefServiceShutdown(); private: - class SystemNetworkContextOwner; class URLLoaderFactoryForSystem; // Returns default set of parameters for configuring the network service. @@ -109,20 +103,9 @@ // network::mojom::ProxyConfigPollerClient implementation: void OnLazyProxyConfigPoll() override; - // The following members are used when the network service is disabled. - URLRequestContextFactory* url_request_context_factory_; - // The system NetworkContext. network::mojom::NetworkContextPtr system_network_context_; - // A helper class that owns the network::NetworkContext that wraps the system - // URLRequestContext. - std::unique_ptr<SystemNetworkContextOwner, - content::BrowserThread::DeleteOnIOThread> - system_network_context_owner_; - - // End of members that are only used if network service is disabled. - // URLLoaderFactory backed by the NetworkContext returned by // GetSystemContext(), so consumers don't all need to create their own // factory.
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc index 61b08e7..a3646ba 100644 --- a/chromecast/browser/url_request_context_factory.cc +++ b/chromecast/browser/url_request_context_factory.cc
@@ -29,7 +29,6 @@ #include "net/cert/ct_policy_enforcer.h" #include "net/cert/ct_policy_status.h" #include "net/cert/multi_log_ct_verifier.h" -#include "net/cert_net/nss_ocsp.h" #include "net/cookies/cookie_store.h" #include "net/dns/host_resolver.h" #include "net/dns/host_resolver_manager.h" @@ -46,7 +45,6 @@ #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_intercepting_job_factory.h" #include "net/url_request/url_request_job_factory_impl.h" -#include "services/network/public/cpp/features.h" namespace chromecast { namespace shell { @@ -81,14 +79,6 @@ request_context_.reset(factory_->CreateMediaRequestContext()); } else { request_context_.reset(factory_->CreateSystemRequestContext()); -#if defined(USE_NSS_CERTS) - // TODO(juke): Migrate callsites of GetURLRequestContext() to - // network::NetworkContext. - if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { - // Set request context used by NSS for Crl requests. - net::SetURLRequestContextForNSSHttpIO(request_context_.get()); - } -#endif // defined(USE_NSS_CERTS) } } return request_context_.get();
diff --git a/chromecast/cast_shell.cmx b/chromecast/cast_shell.cmx index a617943a..3fcbdfe 100644 --- a/chromecast/cast_shell.cmx +++ b/chromecast/cast_shell.cmx
@@ -14,6 +14,7 @@ "services": [ "fuchsia.bluetooth.gatt.Server", "fuchsia.bluetooth.le.Peripheral", + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", "fuchsia.media.Audio",
diff --git a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java index 1f1987d..f8b735f 100644 --- a/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java +++ b/chromecast/media/cma/backend/android/java/src/org/chromium/chromecast/cma/backend/android/VolumeMap.java
@@ -53,23 +53,29 @@ } }; + private static int getStreamMinVolume(int streamType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + return getAudioManager().getStreamMinVolume(streamType); + } + // Try to use reflection in case API is hidden. + try { + return (int) getAudioManager() + .getClass() + .getMethod("getStreamMinVolume", int.class) + .invoke(sAudioManager, streamType); + } catch (Exception e) { + Log.w(TAG, "Unsupported Android SDK version: " + Build.VERSION.SDK_INT, e); + return 0; + } + }; + private static final SparseIntArray MIN_VOLUME_INDEX = new SparseIntArray(4) { { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - append(AudioManager.STREAM_MUSIC, - getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC)); - append(AudioManager.STREAM_ALARM, - getAudioManager().getStreamMinVolume(AudioManager.STREAM_ALARM)); - append(AudioManager.STREAM_SYSTEM, - getAudioManager().getStreamMinVolume(AudioManager.STREAM_SYSTEM)); - append(AudioManager.STREAM_VOICE_CALL, - getAudioManager().getStreamMinVolume(AudioManager.STREAM_VOICE_CALL)); - } else { - append(AudioManager.STREAM_MUSIC, 0); - append(AudioManager.STREAM_ALARM, 0); - append(AudioManager.STREAM_SYSTEM, 0); - append(AudioManager.STREAM_VOICE_CALL, 0); - } + append(AudioManager.STREAM_MUSIC, getStreamMinVolume(AudioManager.STREAM_MUSIC)); + append(AudioManager.STREAM_ALARM, getStreamMinVolume(AudioManager.STREAM_ALARM)); + append(AudioManager.STREAM_SYSTEM, getStreamMinVolume(AudioManager.STREAM_SYSTEM)); + append(AudioManager.STREAM_VOICE_CALL, + getStreamMinVolume(AudioManager.STREAM_VOICE_CALL)); } };
diff --git a/chromeos/components/account_manager/account_manager.cc b/chromeos/components/account_manager/account_manager.cc index 1c9be19..1c31d5b 100644 --- a/chromeos/components/account_manager/account_manager.cc +++ b/chromeos/components/account_manager/account_manager.cc
@@ -60,6 +60,13 @@ token_load_status); } +void RecordInitializationTime( + const base::TimeTicks& initialization_start_time) { + base::UmaHistogramMicrosecondsTimes( + "AccountManager.InitializationTime", + base::TimeTicks::Now() - initialization_start_time); +} + } // namespace // static @@ -174,6 +181,7 @@ PrefService* pref_service) { VLOG(1) << "AccountManager::Initialize"; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + const base::TimeTicks initialization_start_time = base::TimeTicks::Now(); if (init_state_ != InitializationState::kNotStarted) { // |Initialize| has already been called once. To help diagnose possible race @@ -197,7 +205,7 @@ base::BindOnce(&AccountManager::LoadAccountsFromDisk, writer_->path()), base::BindOnce( &AccountManager::InsertAccountsAndRunInitializationCallbacks, - weak_factory_.GetWeakPtr())); + weak_factory_.GetWeakPtr(), initialization_start_time)); } // static @@ -249,12 +257,14 @@ } void AccountManager::InsertAccountsAndRunInitializationCallbacks( + const base::TimeTicks& initialization_start_time, const AccountMap& accounts) { VLOG(1) << "AccountManager::RunInitializationCallbacks"; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); accounts_.insert(accounts.begin(), accounts.end()); init_state_ = InitializationState::kInitialized; + RecordInitializationTime(initialization_start_time); for (auto& cb : initialization_callbacks_) { std::move(cb).Run();
diff --git a/chromeos/components/account_manager/account_manager.h b/chromeos/components/account_manager/account_manager.h index 9c731b21..d8671c63 100644 --- a/chromeos/components/account_manager/account_manager.h +++ b/chromeos/components/account_manager/account_manager.h
@@ -267,7 +267,11 @@ // Reads accounts from |accounts| and inserts them in |accounts_| and runs all // callbacks waiting on |AccountManager| initialization. - void InsertAccountsAndRunInitializationCallbacks(const AccountMap& accounts); + // |initialization_start_time| is the time at which + // |AccountManager::Initialize| was called. + void InsertAccountsAndRunInitializationCallbacks( + const base::TimeTicks& initialization_start_time, + const AccountMap& accounts); // Accepts a closure and runs it immediately if |AccountManager| has already // been initialized, otherwise saves the |closure| for running later, when the
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn index b08d1bd..2deb98f1 100644 --- a/chromeos/dbus/BUILD.gn +++ b/chromeos/dbus/BUILD.gn
@@ -44,6 +44,8 @@ "arc_appfuse_provider_client.h", "arc_camera_client.cc", "arc_camera_client.h", + "arc_keymaster_client.cc", + "arc_keymaster_client.h", "arc_midis_client.cc", "arc_midis_client.h", "arc_obb_mounter_client.cc", @@ -72,6 +74,8 @@ "fake_arc_appfuse_provider_client.h", "fake_arc_camera_client.cc", "fake_arc_camera_client.h", + "fake_arc_keymaster_client.cc", + "fake_arc_keymaster_client.h", "fake_arc_midis_client.cc", "fake_arc_midis_client.h", "fake_arc_obb_mounter_client.cc",
diff --git a/chromeos/dbus/arc_keymaster_client.cc b/chromeos/dbus/arc_keymaster_client.cc new file mode 100644 index 0000000..ef2b1836 --- /dev/null +++ b/chromeos/dbus/arc_keymaster_client.cc
@@ -0,0 +1,70 @@ +// 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 "chromeos/dbus/arc_keymaster_client.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +void OnVoidDBusMethod(VoidDBusMethodCallback callback, + dbus::Response* response) { + std::move(callback).Run(response != nullptr); +} + +class ArcKeymasterClientImpl : public ArcKeymasterClient { + public: + ArcKeymasterClientImpl() = default; + ~ArcKeymasterClientImpl() override = default; + + void BootstrapMojoConnection(base::ScopedFD fd, + VoidDBusMethodCallback callback) override { + dbus::MethodCall method_call( + arc::keymaster::kArcKeymasterInterfaceName, + arc::keymaster::kBootstrapMojoConnectionMethod); + dbus::MessageWriter writer(&method_call); + + writer.AppendFileDescriptor(fd.get()); + proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&OnVoidDBusMethod, std::move(callback))); + } + + protected: + void Init(dbus::Bus* bus) override { + proxy_ = bus->GetObjectProxy( + arc::keymaster::kArcKeymasterServiceName, + dbus::ObjectPath(arc::keymaster::kArcKeymasterServicePath)); + } + + private: + // Owned by the D-Bus implementation, who outlives this class. + dbus::ObjectProxy* proxy_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ArcKeymasterClientImpl); +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ArcKeymasterClient + +ArcKeymasterClient::ArcKeymasterClient() = default; +ArcKeymasterClient::~ArcKeymasterClient() = default; + +// static +std::unique_ptr<ArcKeymasterClient> ArcKeymasterClient::Create() { + return std::make_unique<ArcKeymasterClientImpl>(); +} + +} // namespace chromeos
diff --git a/chromeos/dbus/arc_keymaster_client.h b/chromeos/dbus/arc_keymaster_client.h new file mode 100644 index 0000000..ed28185 --- /dev/null +++ b/chromeos/dbus/arc_keymaster_client.h
@@ -0,0 +1,43 @@ +// 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 CHROMEOS_DBUS_ARC_KEYMASTER_CLIENT_H_ +#define CHROMEOS_DBUS_ARC_KEYMASTER_CLIENT_H_ + +#include <memory> +#include <string> + +#include "base/callback_forward.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "chromeos/dbus/dbus_client.h" +#include "chromeos/dbus/dbus_method_call_status.h" + +namespace chromeos { + +// ArcKeymasterClient is used to bootstrap a Mojo connection with the +// arc-keymasterd daemon in Chrome OS. +class COMPONENT_EXPORT(CHROMEOS_DBUS) ArcKeymasterClient : public DBusClient { + public: + ~ArcKeymasterClient() override; + + // Factory function. + static std::unique_ptr<ArcKeymasterClient> Create(); + + // Bootstrap the Mojo connection between Chrome and the keymaster service. + // Should pass in the child end of the Mojo pipe. + virtual void BootstrapMojoConnection(base::ScopedFD fd, + VoidDBusMethodCallback callback) = 0; + + protected: + // Create() should be used instead. + ArcKeymasterClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(ArcKeymasterClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_ARC_KEYMASTER_CLIENT_H_
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc index ee234f3..a563468 100644 --- a/chromeos/dbus/dbus_clients_browser.cc +++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -6,6 +6,7 @@ #include "base/logging.h" #include "chromeos/dbus/arc_appfuse_provider_client.h" +#include "chromeos/dbus/arc_keymaster_client.h" #include "chromeos/dbus/arc_midis_client.h" #include "chromeos/dbus/arc_obb_mounter_client.h" #include "chromeos/dbus/arc_oemcrypto_client.h" @@ -18,6 +19,7 @@ #include "chromeos/dbus/debug_daemon_client.h" #include "chromeos/dbus/easy_unlock_client.h" #include "chromeos/dbus/fake_arc_appfuse_provider_client.h" +#include "chromeos/dbus/fake_arc_keymaster_client.h" #include "chromeos/dbus/fake_arc_midis_client.h" #include "chromeos/dbus/fake_arc_obb_mounter_client.h" #include "chromeos/dbus/fake_arc_oemcrypto_client.h" @@ -75,6 +77,8 @@ arc_appfuse_provider_client_ = CREATE_DBUS_CLIENT(ArcAppfuseProviderClient, use_real_clients); + arc_keymaster_client_ = + CREATE_DBUS_CLIENT(ArcKeymasterClient, use_real_clients); arc_midis_client_ = CREATE_DBUS_CLIENT(ArcMidisClient, use_real_clients); arc_obb_mounter_client_ = CREATE_DBUS_CLIENT(ArcObbMounterClient, use_real_clients); @@ -116,6 +120,7 @@ DCHECK(DBusThreadManager::IsInitialized()); arc_appfuse_provider_client_->Init(system_bus); + arc_keymaster_client_->Init(system_bus); arc_midis_client_->Init(system_bus); arc_obb_mounter_client_->Init(system_bus); arc_oemcrypto_client_->Init(system_bus);
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h index e7f9404..bc7e1f29 100644 --- a/chromeos/dbus/dbus_clients_browser.h +++ b/chromeos/dbus/dbus_clients_browser.h
@@ -17,6 +17,7 @@ namespace chromeos { class ArcAppfuseProviderClient; +class ArcKeymasterClient; class ArcMidisClient; class ArcObbMounterClient; class ArcOemCryptoClient; @@ -55,6 +56,7 @@ friend class DBusThreadManagerSetter; std::unique_ptr<ArcAppfuseProviderClient> arc_appfuse_provider_client_; + std::unique_ptr<ArcKeymasterClient> arc_keymaster_client_; std::unique_ptr<ArcMidisClient> arc_midis_client_; std::unique_ptr<ArcObbMounterClient> arc_obb_mounter_client_; std::unique_ptr<ArcOemCryptoClient> arc_oemcrypto_client_;
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc index a6460da7..67a66a5 100644 --- a/chromeos/dbus/dbus_thread_manager.cc +++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -11,6 +11,7 @@ #include "base/message_loop/message_loop.h" #include "base/system/sys_info.h" #include "base/threading/thread.h" +#include "chromeos/dbus/arc_keymaster_client.h" #include "chromeos/dbus/arc_midis_client.h" #include "chromeos/dbus/arc_obb_mounter_client.h" #include "chromeos/dbus/arc_oemcrypto_client.h" @@ -111,6 +112,11 @@ : nullptr; } +ArcKeymasterClient* DBusThreadManager::GetArcKeymasterClient() { + return clients_browser_ ? clients_browser_->arc_keymaster_client_.get() + : nullptr; +} + ArcMidisClient* DBusThreadManager::GetArcMidisClient() { return clients_browser_ ? clients_browser_->arc_midis_client_.get() : nullptr; }
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h index f3e508a..3141542 100644 --- a/chromeos/dbus/dbus_thread_manager.h +++ b/chromeos/dbus/dbus_thread_manager.h
@@ -25,6 +25,7 @@ // Style Note: Clients are sorted by names. class ArcAppfuseProviderClient; +class ArcKeymasterClient; class ArcMidisClient; class ArcObbMounterClient; class ArcOemCryptoClient; @@ -116,6 +117,7 @@ // TODO(jamescook): Replace this with calls to FooClient::Get(). // http://crbug.com/647367 ArcAppfuseProviderClient* GetArcAppfuseProviderClient(); + ArcKeymasterClient* GetArcKeymasterClient(); ArcMidisClient* GetArcMidisClient(); ArcObbMounterClient* GetArcObbMounterClient(); ArcOemCryptoClient* GetArcOemCryptoClient();
diff --git a/chromeos/dbus/fake_arc_keymaster_client.cc b/chromeos/dbus/fake_arc_keymaster_client.cc new file mode 100644 index 0000000..ef79e23 --- /dev/null +++ b/chromeos/dbus/fake_arc_keymaster_client.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 "chromeos/dbus/fake_arc_keymaster_client.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/threading/thread_task_runner_handle.h" + +namespace chromeos { + +void FakeArcKeymasterClient::Init(dbus::Bus* bus) {} + +void FakeArcKeymasterClient::BootstrapMojoConnection( + base::ScopedFD fd, + VoidDBusMethodCallback callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), false)); +} + +} // namespace chromeos
diff --git a/chromeos/dbus/fake_arc_keymaster_client.h b/chromeos/dbus/fake_arc_keymaster_client.h new file mode 100644 index 0000000..9e28cecd --- /dev/null +++ b/chromeos/dbus/fake_arc_keymaster_client.h
@@ -0,0 +1,32 @@ +// 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 CHROMEOS_DBUS_FAKE_ARC_KEYMASTER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_ARC_KEYMASTER_CLIENT_H_ + +#include "chromeos/dbus/arc_keymaster_client.h" + +namespace chromeos { + +// A fake implementation of ArcKeymasterClient. +class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeArcKeymasterClient + : public ArcKeymasterClient { + public: + FakeArcKeymasterClient() = default; + ~FakeArcKeymasterClient() override = default; + + // DBusClient override: + void Init(dbus::Bus* bus) override; + + // ArcKeymasterClient override: + void BootstrapMojoConnection(base::ScopedFD fd, + VoidDBusMethodCallback callback) override; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeArcKeymasterClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_ARC_KEYMASTER_CLIENT_H_
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn index 91b86c4..cf81320 100644 --- a/chromeos/services/device_sync/BUILD.gn +++ b/chromeos/services/device_sync/BUILD.gn
@@ -50,6 +50,10 @@ "cryptauth_gcm_manager.h", "cryptauth_gcm_manager_impl.cc", "cryptauth_gcm_manager_impl.h", + "cryptauth_group_private_key_sharer.cc", + "cryptauth_group_private_key_sharer.h", + "cryptauth_group_private_key_sharer_impl.cc", + "cryptauth_group_private_key_sharer_impl.h", "cryptauth_key.cc", "cryptauth_key.h", "cryptauth_key_bundle.cc", @@ -213,6 +217,7 @@ "cryptauth_enroller_impl_unittest.cc", "cryptauth_enrollment_manager_impl_unittest.cc", "cryptauth_gcm_manager_impl_unittest.cc", + "cryptauth_group_private_key_sharer_impl_unittest.cc", "cryptauth_key_bundle_unittest.cc", "cryptauth_key_creator_impl_unittest.cc", "cryptauth_key_proof_computer_impl_unittest.cc",
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer.cc b/chromeos/services/device_sync/cryptauth_group_private_key_sharer.cc new file mode 100644 index 0000000..5d3391f --- /dev/null +++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer.cc
@@ -0,0 +1,44 @@ +// 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 "chromeos/services/device_sync/cryptauth_group_private_key_sharer.h" + +#include <utility> + +#include "chromeos/services/device_sync/cryptauth_key.h" + +namespace chromeos { + +namespace device_sync { + +CryptAuthGroupPrivateKeySharer::CryptAuthGroupPrivateKeySharer() = default; + +CryptAuthGroupPrivateKeySharer::~CryptAuthGroupPrivateKeySharer() = default; + +void CryptAuthGroupPrivateKeySharer::ShareGroupPrivateKey( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const IdToEncryptingKeyMap& id_to_encrypting_key_map, + ShareGroupPrivateKeyAttemptFinishedCallback callback) { + // Enforce that ShareGroupPrivateKey() can only be called once. + DCHECK(!was_share_group_private_key_called_); + was_share_group_private_key_called_ = true; + + DCHECK(!group_key.private_key().empty()); + DCHECK(!id_to_encrypting_key_map.empty()); + + callback_ = std::move(callback); + + OnAttemptStarted(request_context, group_key, id_to_encrypting_key_map); +} + +void CryptAuthGroupPrivateKeySharer::OnAttemptFinished( + const CryptAuthDeviceSyncResult::ResultCode& device_sync_result_code) { + DCHECK(callback_); + std::move(callback_).Run(device_sync_result_code); +} + +} // namespace device_sync + +} // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer.h b/chromeos/services/device_sync/cryptauth_group_private_key_sharer.h new file mode 100644 index 0000000..ae43044 --- /dev/null +++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer.h
@@ -0,0 +1,74 @@ +// 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 CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_H_ +#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_H_ + +#include <string> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "chromeos/services/device_sync/cryptauth_device_sync_result.h" + +namespace cryptauthv2 { +class RequestContext; +} // namespace cryptauthv2 + +namespace chromeos { + +namespace device_sync { + +class CryptAuthKey; + +// Handles the ShareGroupPrivateKey portion of the CryptAuth v2 DeviceSync +// protocol. Sends the group private key--encrypted with each requesting +// device's public key--to CryptAuth for distribution to the requesting devices +// during their next DeviceSync calls. +// +// A CryptAuthGroupPrivateKeySharer object is designed to be used for only one +// ShareGroupPrivateKey() call. For a new attempt, a new object should be +// created. +class CryptAuthGroupPrivateKeySharer { + public: + using IdToEncryptingKeyMap = base::flat_map<std::string, std::string>; + using ShareGroupPrivateKeyAttemptFinishedCallback = + base::OnceCallback<void(const CryptAuthDeviceSyncResult::ResultCode&)>; + + virtual ~CryptAuthGroupPrivateKeySharer(); + + // Starts the ShareGroupPrivateKey portion of the CryptAuth v2 DeviceSync + // flow. Sends |group_key|'s private key to CryptAuth, encrypted for each + // device with ID in |id_to_encrypting_key| using the corresponding encrypting + // key. |group_key|'s private key and |id_to_encrypting_key_map| cannot be + // empty. + void ShareGroupPrivateKey( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const IdToEncryptingKeyMap& id_to_encrypting_key_map, + ShareGroupPrivateKeyAttemptFinishedCallback callback); + + protected: + CryptAuthGroupPrivateKeySharer(); + + virtual void OnAttemptStarted( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const IdToEncryptingKeyMap& id_to_encrypting_key_map) = 0; + + void OnAttemptFinished( + const CryptAuthDeviceSyncResult::ResultCode& device_sync_result_code); + + private: + ShareGroupPrivateKeyAttemptFinishedCallback callback_; + bool was_share_group_private_key_called_ = false; + + DISALLOW_COPY_AND_ASSIGN(CryptAuthGroupPrivateKeySharer); +}; + +} // namespace device_sync + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_H_
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.cc b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.cc new file mode 100644 index 0000000..4190261 --- /dev/null +++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.cc
@@ -0,0 +1,318 @@ +// 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 "chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h" + +#include <utility> + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/no_destructor.h" +#include "chromeos/components/multidevice/logging/logging.h" +#include "chromeos/services/device_sync/cryptauth_client.h" +#include "chromeos/services/device_sync/cryptauth_ecies_encryptor_impl.h" +#include "chromeos/services/device_sync/cryptauth_key.h" +#include "crypto/sha2.h" + +namespace chromeos { + +namespace device_sync { + +namespace { + +// Timeout values for asynchronous operations. +// TODO(https://crbug.com/933656): Tune these values. +constexpr base::TimeDelta kWaitingForGroupPrivateKeyEncryptionTimeout = + base::TimeDelta::FromSeconds(10); +constexpr base::TimeDelta kWaitingForShareGroupPrivateKeyResponseTimeout = + base::TimeDelta::FromSeconds(10); + +CryptAuthDeviceSyncResult::ResultCode +ShareGroupPrivateKeyNetworkRequestErrorToResultCode(NetworkRequestError error) { + switch (error) { + case NetworkRequestError::kOffline: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallOffline; + case NetworkRequestError::kEndpointNotFound: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallEndpointNotFound; + case NetworkRequestError::kAuthenticationError: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallAuthenticationError; + case NetworkRequestError::kBadRequest: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallBadRequest; + case NetworkRequestError::kResponseMalformed: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallResponseMalformed; + case NetworkRequestError::kInternalServerError: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallInternalServerError; + case NetworkRequestError::kUnknown: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallUnknownError; + } +} + +int64_t CalculateInt64Sha256Hash(const std::string& str) { + int64_t hash; + crypto::SHA256HashString(str, &hash, sizeof(int64_t)); + return hash; +} + +} // namespace + +// static +CryptAuthGroupPrivateKeySharerImpl::Factory* + CryptAuthGroupPrivateKeySharerImpl::Factory::test_factory_ = nullptr; + +// static +CryptAuthGroupPrivateKeySharerImpl::Factory* +CryptAuthGroupPrivateKeySharerImpl::Factory::Get() { + if (test_factory_) + return test_factory_; + + static base::NoDestructor<CryptAuthGroupPrivateKeySharerImpl::Factory> + factory; + return factory.get(); +} + +// static +void CryptAuthGroupPrivateKeySharerImpl::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +CryptAuthGroupPrivateKeySharerImpl::Factory::~Factory() = default; + +std::unique_ptr<CryptAuthGroupPrivateKeySharer> +CryptAuthGroupPrivateKeySharerImpl::Factory::BuildInstance( + CryptAuthClientFactory* client_factory, + std::unique_ptr<base::OneShotTimer> timer) { + return base::WrapUnique( + new CryptAuthGroupPrivateKeySharerImpl(client_factory, std::move(timer))); +} + +CryptAuthGroupPrivateKeySharerImpl::CryptAuthGroupPrivateKeySharerImpl( + CryptAuthClientFactory* client_factory, + std::unique_ptr<base::OneShotTimer> timer) + : client_factory_(client_factory), timer_(std::move(timer)) { + DCHECK(client_factory); +} + +CryptAuthGroupPrivateKeySharerImpl::~CryptAuthGroupPrivateKeySharerImpl() = + default; + +// static +base::Optional<base::TimeDelta> +CryptAuthGroupPrivateKeySharerImpl::GetTimeoutForState(State state) { + switch (state) { + case State::kWaitingForGroupPrivateKeyEncryption: + return kWaitingForGroupPrivateKeyEncryptionTimeout; + case State::kWaitingForShareGroupPrivateKeyResponse: + return kWaitingForShareGroupPrivateKeyResponseTimeout; + default: + // Signifies that there should not be a timeout. + return base::nullopt; + } +} + +// static +base::Optional<CryptAuthDeviceSyncResult::ResultCode> +CryptAuthGroupPrivateKeySharerImpl::ResultCodeErrorFromTimeoutDuringState( + State state) { + switch (state) { + case State::kWaitingForGroupPrivateKeyEncryption: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorTimeoutWaitingForGroupPrivateKeyEncryption; + case State::kWaitingForShareGroupPrivateKeyResponse: + return CryptAuthDeviceSyncResult::ResultCode:: + kErrorTimeoutWaitingForShareGroupPrivateKeyResponse; + default: + return base::nullopt; + } +} + +void CryptAuthGroupPrivateKeySharerImpl::SetState(State state) { + timer_->Stop(); + + PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state; + state_ = state; + + base::Optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state); + if (!timeout_for_state) + return; + + // TODO(https://crbug.com/936273): Add metrics to track failure rates due to + // async timeouts. + timer_->Start(FROM_HERE, *timeout_for_state, + base::BindOnce(&CryptAuthGroupPrivateKeySharerImpl::OnTimeout, + base::Unretained(this))); +} + +void CryptAuthGroupPrivateKeySharerImpl::OnTimeout() { + // If there's a timeout specified, there should be a corresponding error code. + base::Optional<CryptAuthDeviceSyncResult::ResultCode> error_code = + ResultCodeErrorFromTimeoutDuringState(state_); + DCHECK(error_code); + + FinishAttempt(*error_code); +} + +void CryptAuthGroupPrivateKeySharerImpl::OnAttemptStarted( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const IdToEncryptingKeyMap& id_to_encrypting_key_map) { + DCHECK_EQ(State::kNotStarted, state_); + DCHECK(!group_key.private_key().empty()); + + CryptAuthEciesEncryptor::IdToInputMap group_private_keys_to_encrypt; + for (const auto& id_encrypting_key_pair : id_to_encrypting_key_map) { + const std::string& id = id_encrypting_key_pair.first; + const std::string& encrypting_key = id_encrypting_key_pair.second; + + // If the encrypting key is empty, the group private key cannot be + // encrypted. Skip this ID and attempt to encrypt the group private key for + // as many IDs as possible. + // TODO(https://crbug.com/936273): Add metrics for empty device public keys. + if (encrypting_key.empty()) { + PA_LOG(ERROR) << "Cannot encrypt group private key for device with ID " + << id << ". Encrypting key is empty."; + did_non_fatal_error_occur_ = true; + continue; + } + + group_private_keys_to_encrypt[id] = CryptAuthEciesEncryptor::PayloadAndKey( + group_key.private_key(), encrypting_key); + } + + // All encrypting keys are empty; encryption not possible. + if (group_private_keys_to_encrypt.empty()) { + FinishAttempt( + CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey); + return; + } + + SetState(State::kWaitingForGroupPrivateKeyEncryption); + + encryptor_ = CryptAuthEciesEncryptorImpl::Factory::Get()->BuildInstance(); + encryptor_->BatchEncrypt( + group_private_keys_to_encrypt, + base::BindOnce( + &CryptAuthGroupPrivateKeySharerImpl::OnGroupPrivateKeysEncrypted, + base::Unretained(this), request_context, group_key)); +} + +void CryptAuthGroupPrivateKeySharerImpl::OnGroupPrivateKeysEncrypted( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const CryptAuthEciesEncryptor::IdToOutputMap& + id_to_encrypted_group_private_key_map) { + DCHECK_EQ(State::kWaitingForGroupPrivateKeyEncryption, state_); + + cryptauthv2::ShareGroupPrivateKeyRequest request; + request.mutable_context()->CopyFrom(request_context); + + for (const auto& id_encrypted_key_pair : + id_to_encrypted_group_private_key_map) { + // If the group private key could not be encrypted for this ID--due to an + // invalid encrypting key, for instance--skip it. Continue to share as many + // encrypted group private keys as possible. + // TODO(https://crbug.com/936273): Add metrics for group private key + // encryption failures. + bool was_encryption_successful = id_encrypted_key_pair.second.has_value(); + if (!was_encryption_successful) { + PA_LOG(ERROR) << "Group private key could not be encrypted for device " + << "with ID " << id_encrypted_key_pair.first; + did_non_fatal_error_occur_ = true; + continue; + } + + cryptauthv2::EncryptedGroupPrivateKey* encrypted_key = + request.add_encrypted_group_private_keys(); + encrypted_key->set_recipient_device_id(id_encrypted_key_pair.first); + encrypted_key->set_sender_device_id(request_context.device_id()); + encrypted_key->set_encrypted_private_key(*id_encrypted_key_pair.second); + + // CryptAuth requires a SHA-256 hash of the group public key as an int64. + encrypted_key->set_group_public_key_hash( + CalculateInt64Sha256Hash(group_key.public_key())); + } + + // All encryption attempts failed; nothing to share. + if (request.encrypted_group_private_keys().empty()) { + FinishAttempt( + CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey); + return; + } + + SetState(State::kWaitingForShareGroupPrivateKeyResponse); + + cryptauth_client_ = client_factory_->CreateInstance(); + cryptauth_client_->ShareGroupPrivateKey( + request, + base::Bind( + &CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeySuccess, + base::Unretained(this)), + base::Bind( + &CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeyFailure, + base::Unretained(this))); +} + +void CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeySuccess( + const cryptauthv2::ShareGroupPrivateKeyResponse& response) { + DCHECK_EQ(State::kWaitingForShareGroupPrivateKeyResponse, state_); + + CryptAuthDeviceSyncResult::ResultCode result_code = + did_non_fatal_error_occur_ + ? CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors + : CryptAuthDeviceSyncResult::ResultCode::kSuccess; + FinishAttempt(result_code); +} + +void CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeyFailure( + NetworkRequestError error) { + DCHECK_EQ(State::kWaitingForShareGroupPrivateKeyResponse, state_); + + FinishAttempt(ShareGroupPrivateKeyNetworkRequestErrorToResultCode(error)); +} + +void CryptAuthGroupPrivateKeySharerImpl::FinishAttempt( + const CryptAuthDeviceSyncResult::ResultCode& result_code) { + encryptor_.reset(); + cryptauth_client_.reset(); + + SetState(State::kFinished); + + OnAttemptFinished(result_code); +} + +std::ostream& operator<<( + std::ostream& stream, + const CryptAuthGroupPrivateKeySharerImpl::State& state) { + switch (state) { + case CryptAuthGroupPrivateKeySharerImpl::State::kNotStarted: + stream << "[GroupPrivateKeySharer state: Not started]"; + break; + case CryptAuthGroupPrivateKeySharerImpl::State:: + kWaitingForGroupPrivateKeyEncryption: + stream << "[GroupPrivateKeySharer state: Waiting for group private key " + << "encryption]"; + break; + case CryptAuthGroupPrivateKeySharerImpl::State:: + kWaitingForShareGroupPrivateKeyResponse: + stream << "[GroupPrivateKeySharer state: Waiting for " + << "ShareGroupPrivateKey response]"; + break; + case CryptAuthGroupPrivateKeySharerImpl::State::kFinished: + stream << "[GroupPrivateKeySharer state: Finished]"; + break; + } + + return stream; +} + +} // namespace device_sync + +} // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h new file mode 100644 index 0000000..aa7bd17 --- /dev/null +++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h
@@ -0,0 +1,106 @@ +// 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 CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_IMPL_H_ +#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_IMPL_H_ + +#include <memory> +#include <ostream> +#include <string> + +#include "base/macros.h" +#include "base/timer/timer.h" +#include "chromeos/services/device_sync/cryptauth_device_sync_result.h" +#include "chromeos/services/device_sync/cryptauth_ecies_encryptor.h" +#include "chromeos/services/device_sync/cryptauth_group_private_key_sharer.h" +#include "chromeos/services/device_sync/network_request_error.h" +#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h" + +namespace chromeos { + +namespace device_sync { + +class CryptAuthClient; +class CryptAuthClientFactory; +class CryptAuthKey; + +// An implementation of CryptAuthGroupPrivateKeySharer, using instances of +// CryptAuthClient to make the ShareGroupPrivateKey API calls to CryptAuth. +// Timeouts are handled internally, so ShareGroupPrivateKey() is always +// guaranteed to return. +class CryptAuthGroupPrivateKeySharerImpl + : public CryptAuthGroupPrivateKeySharer { + public: + class Factory { + public: + static Factory* Get(); + static void SetFactoryForTesting(Factory* test_factory); + virtual ~Factory(); + virtual std::unique_ptr<CryptAuthGroupPrivateKeySharer> BuildInstance( + CryptAuthClientFactory* client_factory, + std::unique_ptr<base::OneShotTimer> timer = + std::make_unique<base::OneShotTimer>()); + + private: + static Factory* test_factory_; + }; + + ~CryptAuthGroupPrivateKeySharerImpl() override; + + private: + enum class State { + kNotStarted, + kWaitingForGroupPrivateKeyEncryption, + kWaitingForShareGroupPrivateKeyResponse, + kFinished + }; + + friend std::ostream& operator<<(std::ostream& stream, const State& state); + + static base::Optional<base::TimeDelta> GetTimeoutForState(State state); + static base::Optional<CryptAuthDeviceSyncResult::ResultCode> + ResultCodeErrorFromTimeoutDuringState(State state); + + CryptAuthGroupPrivateKeySharerImpl(CryptAuthClientFactory* client_factory, + std::unique_ptr<base::OneShotTimer> timer); + + // CryptAuthGroupPrivateKeySharer: + void OnAttemptStarted( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const IdToEncryptingKeyMap& id_to_encrypting_key_map) override; + + void SetState(State state); + void OnTimeout(); + + void OnGroupPrivateKeysEncrypted( + const cryptauthv2::RequestContext& request_context, + const CryptAuthKey& group_key, + const CryptAuthEciesEncryptor::IdToOutputMap& + id_to_encrypted_group_private_key_map); + void OnShareGroupPrivateKeySuccess( + const cryptauthv2::ShareGroupPrivateKeyResponse& response); + void OnShareGroupPrivateKeyFailure(NetworkRequestError error); + + void FinishAttempt(const CryptAuthDeviceSyncResult::ResultCode& result_code); + + // Used for batch encrypting the group private key with each encrypting key. + std::unique_ptr<CryptAuthEciesEncryptor> encryptor_; + + // Used to make the ShareGroupPrivateKey API call to CryptAuth. + std::unique_ptr<CryptAuthClient> cryptauth_client_; + + bool did_non_fatal_error_occur_ = false; + State state_ = State::kNotStarted; + CryptAuthClientFactory* client_factory_ = nullptr; + std::unique_ptr<base::OneShotTimer> timer_; + + DISALLOW_COPY_AND_ASSIGN(CryptAuthGroupPrivateKeySharerImpl); +}; + +} // namespace device_sync + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_GROUP_PRIVATE_KEY_SHARER_IMPL_H_
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc new file mode 100644 index 0000000..9ed2ccd --- /dev/null +++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
@@ -0,0 +1,433 @@ +// 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 "chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h" + +#include <memory> +#include <string> +#include <utility> + +#include "base/containers/flat_set.h" +#include "base/macros.h" +#include "base/no_destructor.h" +#include "base/optional.h" +#include "base/timer/mock_timer.h" +#include "chromeos/services/device_sync/cryptauth_client.h" +#include "chromeos/services/device_sync/cryptauth_device.h" +#include "chromeos/services/device_sync/cryptauth_device_sync_result.h" +#include "chromeos/services/device_sync/cryptauth_ecies_encryptor_impl.h" +#include "chromeos/services/device_sync/cryptauth_key.h" +#include "chromeos/services/device_sync/cryptauth_key_bundle.h" +#include "chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.h" +#include "chromeos/services/device_sync/fake_cryptauth_ecies_encryptor.h" +#include "chromeos/services/device_sync/fake_ecies_encryption.h" +#include "chromeos/services/device_sync/mock_cryptauth_client.h" +#include "chromeos/services/device_sync/network_request_error.h" +#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h" +#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h" +#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h" +#include "crypto/sha2.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace device_sync { + +namespace { + +const char kAccessTokenUsed[] = "access token used by CryptAuthClient"; + +const cryptauthv2::ClientMetadata& GetClientMetadata() { + static const base::NoDestructor<cryptauthv2::ClientMetadata> client_metadata( + cryptauthv2::BuildClientMetadata(0 /* retry_count */, + cryptauthv2::ClientMetadata::PERIODIC)); + return *client_metadata; +} + +const cryptauthv2::RequestContext& GetRequestContext() { + static const base::NoDestructor<cryptauthv2::RequestContext> request_context( + [] { + return cryptauthv2::BuildRequestContext( + CryptAuthKeyBundle::KeyBundleNameEnumToString( + CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether), + GetClientMetadata(), + cryptauthv2::GetClientAppMetadataForTest().instance_id(), + cryptauthv2::GetClientAppMetadataForTest().instance_id_token()); + }()); + return *request_context; +} + +const CryptAuthKey& GetGroupKey() { + static const base::NoDestructor<CryptAuthKey> group_key([] { + return CryptAuthKey( + kGroupPublicKey, GetPrivateKeyFromPublicKeyForTest(kGroupPublicKey), + CryptAuthKey::Status::kActive, cryptauthv2::KeyType::P256); + }()); + return *group_key; +} + +CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap +IdToEncryptingKeyMapFromDeviceIds( + const base::flat_set<std::string>& device_ids) { + CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap id_to_encrypting_key_map; + for (const std::string& id : device_ids) { + id_to_encrypting_key_map.insert_or_assign( + id, GetTestDeviceWithId(id).device_better_together_public_key); + } + + return id_to_encrypting_key_map; +} + +} // namespace + +class DeviceSyncCryptAuthGroupPrivateKeySharerImplTest + : public testing::Test, + public MockCryptAuthClientFactory::Observer { + protected: + DeviceSyncCryptAuthGroupPrivateKeySharerImplTest() + : client_factory_(std::make_unique<MockCryptAuthClientFactory>( + MockCryptAuthClientFactory::MockType::MAKE_NICE_MOCKS)), + fake_cryptauth_ecies_encryptor_factory_( + std::make_unique<FakeCryptAuthEciesEncryptorFactory>()) { + client_factory_->AddObserver(this); + } + + ~DeviceSyncCryptAuthGroupPrivateKeySharerImplTest() override { + client_factory_->RemoveObserver(this); + } + + // testing::Test: + void SetUp() override { + CryptAuthEciesEncryptorImpl::Factory::SetFactoryForTesting( + fake_cryptauth_ecies_encryptor_factory_.get()); + + auto mock_timer = std::make_unique<base::MockOneShotTimer>(); + timer_ = mock_timer.get(); + + sharer_ = CryptAuthGroupPrivateKeySharerImpl::Factory::Get()->BuildInstance( + client_factory_.get(), std::move(mock_timer)); + } + + // testing::Test: + void TearDown() override { + CryptAuthEciesEncryptorImpl::Factory::SetFactoryForTesting(nullptr); + } + + // MockCryptAuthClientFactory::Observer: + void OnCryptAuthClientCreated(MockCryptAuthClient* client) override { + ON_CALL(*client, ShareGroupPrivateKey(testing::_, testing::_, testing::_)) + .WillByDefault( + Invoke(this, &DeviceSyncCryptAuthGroupPrivateKeySharerImplTest:: + OnShareGroupPrivateKey)); + + ON_CALL(*client, GetAccessTokenUsed()) + .WillByDefault(testing::Return(kAccessTokenUsed)); + } + + void ShareGroupPrivateKey( + const CryptAuthKey& group_key, + const CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap& + id_to_encrypting_key_map) { + group_key_ = std::make_unique<CryptAuthKey>(group_key); + id_to_encrypting_key_map_ = id_to_encrypting_key_map; + + sharer_->ShareGroupPrivateKey( + GetRequestContext(), group_key, id_to_encrypting_key_map, + base::BindOnce(&DeviceSyncCryptAuthGroupPrivateKeySharerImplTest:: + OnGetFeatureStatusesComplete, + base::Unretained(this))); + } + + // Fail encryption for IDs in |device_ids_to_fail|. Encryption could fail if + // the input encrypting key is invalid, for instance. + void RunGroupPrivateKeyEncryptor( + const base::flat_set<std::string>& expected_device_ids, + const base::flat_set<std::string>& device_ids_to_fail) { + ASSERT_EQ(expected_device_ids.size(), + encryptor()->id_to_input_map().size()); + + for (const auto& id_payload_and_key_pair : encryptor()->id_to_input_map()) { + const std::string& id = id_payload_and_key_pair.first; + const std::string& payload = id_payload_and_key_pair.second.payload; + const std::string& encrypting_key = id_payload_and_key_pair.second.key; + + EXPECT_TRUE(base::Contains(expected_device_ids, id)); + + // Verify that encryptor inputs agrees with ShareGroupPrivateKey() inputs. + const auto it = id_to_encrypting_key_map_.find(id); + ASSERT_NE(id_to_encrypting_key_map_.end(), it); + EXPECT_EQ(it->second, encrypting_key); + ASSERT_TRUE(group_key_); + ASSERT_TRUE(!group_key_->private_key().empty()); + EXPECT_EQ(group_key_->private_key(), payload); + + id_to_encrypted_group_private_key_map_[id] = + base::Contains(device_ids_to_fail, id) + ? base::nullopt + : base::make_optional<std::string>( + MakeFakeEncryptedString(payload, encrypting_key)); + } + + encryptor()->FinishAttempt(FakeCryptAuthEciesEncryptor::Action::kEncryption, + id_to_encrypted_group_private_key_map_); + } + + // Ensures that ShareGroupPrivateKeyRequest is consistent with the output from + // the encryptor, |id_to_encrypted_group_private_key_map_|. + void VerifyShareGroupPrivateKeyRequest( + const base::flat_set<std::string>& expected_device_ids) { + ASSERT_TRUE(share_group_private_key_request_); + EXPECT_TRUE(share_group_private_key_success_callback_); + EXPECT_TRUE(share_group_private_key_failure_callback_); + + EXPECT_EQ(GetRequestContext().SerializeAsString(), + share_group_private_key_request_->context().SerializeAsString()); + EXPECT_EQ( + static_cast<int>(expected_device_ids.size()), + share_group_private_key_request_->encrypted_group_private_keys_size()); + + ASSERT_TRUE(group_key_); + int64_t expected_group_public_key_hash; + crypto::SHA256HashString(group_key_->public_key(), + &expected_group_public_key_hash, sizeof(int64_t)); + + for (const cryptauthv2::EncryptedGroupPrivateKey& request_encrypted_key : + share_group_private_key_request_->encrypted_group_private_keys()) { + const std::string& recipient_id = + request_encrypted_key.recipient_device_id(); + + const auto expected_it = + id_to_encrypted_group_private_key_map_.find(recipient_id); + ASSERT_NE(id_to_encrypted_group_private_key_map_.end(), expected_it); + ASSERT_TRUE(expected_it->second); + + EXPECT_EQ(GetRequestContext().device_id(), + request_encrypted_key.sender_device_id()); + EXPECT_EQ(expected_group_public_key_hash, + request_encrypted_key.group_public_key_hash()); + EXPECT_EQ(*expected_it->second, + request_encrypted_key.encrypted_private_key()); + + // Verify that the encrypted group private key can be decrypted with the + // recipient device's private key. + std::string recipient_device_better_together_private_key = + GetPrivateKeyFromPublicKeyForTest( + GetTestDeviceWithId(recipient_id) + .device_better_together_public_key); + EXPECT_EQ(group_key_->private_key(), + DecryptFakeEncryptedString( + request_encrypted_key.encrypted_private_key(), + recipient_device_better_together_private_key)); + } + } + + void SendShareGroupPrivateKeyResponse() { + ASSERT_TRUE(share_group_private_key_success_callback_); + std::move(share_group_private_key_success_callback_) + .Run(cryptauthv2::ShareGroupPrivateKeyResponse()); + } + + void FailShareGroupPrivateKeyRequest( + const NetworkRequestError& network_request_error) { + ASSERT_TRUE(share_group_private_key_failure_callback_); + std::move(share_group_private_key_failure_callback_) + .Run(network_request_error); + } + + void VerifyShareGroupPrivateKeyResult( + const CryptAuthDeviceSyncResult::ResultCode& expected_result_code) { + ASSERT_TRUE(device_sync_result_code_); + EXPECT_EQ(expected_result_code, device_sync_result_code_); + } + + base::MockOneShotTimer* timer() { return timer_; } + + private: + FakeCryptAuthEciesEncryptor* encryptor() { + return fake_cryptauth_ecies_encryptor_factory_->instance(); + } + + void OnShareGroupPrivateKey( + const cryptauthv2::ShareGroupPrivateKeyRequest& request, + const CryptAuthClient::ShareGroupPrivateKeyCallback& callback, + const CryptAuthClient::ErrorCallback& error_callback) { + EXPECT_FALSE(share_group_private_key_request_); + EXPECT_FALSE(share_group_private_key_success_callback_); + EXPECT_FALSE(share_group_private_key_failure_callback_); + + share_group_private_key_request_ = request; + share_group_private_key_success_callback_ = callback; + share_group_private_key_failure_callback_ = error_callback; + } + + void OnGetFeatureStatusesComplete( + const CryptAuthDeviceSyncResult::ResultCode& device_sync_result_code) { + device_sync_result_code_ = device_sync_result_code; + } + + std::unique_ptr<CryptAuthKey> group_key_; + CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap + id_to_encrypting_key_map_; + + base::Optional<cryptauthv2::ShareGroupPrivateKeyRequest> + share_group_private_key_request_; + CryptAuthClient::ShareGroupPrivateKeyCallback + share_group_private_key_success_callback_; + CryptAuthClient::ErrorCallback share_group_private_key_failure_callback_; + + CryptAuthEciesEncryptor::IdToOutputMap id_to_encrypted_group_private_key_map_; + + base::Optional<CryptAuthDeviceSyncResult::ResultCode> + device_sync_result_code_; + + std::unique_ptr<MockCryptAuthClientFactory> client_factory_; + std::unique_ptr<FakeCryptAuthEciesEncryptorFactory> + fake_cryptauth_ecies_encryptor_factory_; + base::MockOneShotTimer* timer_; + + std::unique_ptr<CryptAuthGroupPrivateKeySharer> sharer_; + + DISALLOW_COPY_AND_ASSIGN(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest); +}; + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, Success) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + RunGroupPrivateKeyEncryptor(device_ids, {} /* device_ids_to_fail */); + + VerifyShareGroupPrivateKeyRequest(device_ids); + + SendShareGroupPrivateKeyResponse(); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode::kSuccess); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + FinishedWithNonFatalErrors_SingleEncryptionFails) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + // Encryption fails for a remote device. + std::string encryption_failure_device_id = + GetRemoteDeviceNeedsGroupPrivateKeyForTest().instance_id(); + RunGroupPrivateKeyEncryptor( + device_ids, {encryption_failure_device_id} /* device_ids_to_fail */); + + base::flat_set<std::string> expected_device_ids = device_ids; + expected_device_ids.erase(encryption_failure_device_id); + VerifyShareGroupPrivateKeyRequest(expected_device_ids); + + SendShareGroupPrivateKeyResponse(); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + Failure_AllEncryptionsFails) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + // Encryption fails for all devices. + RunGroupPrivateKeyEncryptor(device_ids, device_ids /* device_ids_to_fail */); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + FinishedWithNonFatalErrors_SingleEncryptionKeyEmpty) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap + id_to_encrypting_key_map = IdToEncryptingKeyMapFromDeviceIds(device_ids); + + // A remote device has an empty encrypting key. + std::string empty_encrypting_key_device_id = + GetRemoteDeviceNeedsGroupPrivateKeyForTest().instance_id(); + id_to_encrypting_key_map[empty_encrypting_key_device_id].clear(); + ShareGroupPrivateKey(GetGroupKey(), id_to_encrypting_key_map); + + base::flat_set<std::string> expected_device_ids = device_ids; + expected_device_ids.erase(empty_encrypting_key_device_id); + RunGroupPrivateKeyEncryptor(expected_device_ids, {} /* device_ids_to_fail */); + + VerifyShareGroupPrivateKeyRequest(expected_device_ids); + + SendShareGroupPrivateKeyResponse(); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + Failure_AllEncryptionKeysEmpty) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + + // All devices have an empty encrypting key. + CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap + id_to_encrypting_key_map = IdToEncryptingKeyMapFromDeviceIds(device_ids); + for (auto& id_encrypting_key_pair : id_to_encrypting_key_map) + id_encrypting_key_pair.second.clear(); + + ShareGroupPrivateKey(GetGroupKey(), id_to_encrypting_key_map); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + Failure_Timeout_Encryption) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + timer()->Fire(); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode:: + kErrorTimeoutWaitingForGroupPrivateKeyEncryption); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + Failure_Timeout_ShareGroupPrivateKeyRequest) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + RunGroupPrivateKeyEncryptor(device_ids, {} /* device_ids_to_fail */); + + VerifyShareGroupPrivateKeyRequest(device_ids); + + timer()->Fire(); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode:: + kErrorTimeoutWaitingForShareGroupPrivateKeyResponse); +} + +TEST_F(DeviceSyncCryptAuthGroupPrivateKeySharerImplTest, + Failure_ApiCall_ShareGroupPrivateKey) { + base::flat_set<std::string> device_ids = GetAllTestDeviceIds(); + ShareGroupPrivateKey(GetGroupKey(), + IdToEncryptingKeyMapFromDeviceIds(device_ids)); + + RunGroupPrivateKeyEncryptor(device_ids, {} /* device_ids_to_fail */); + + VerifyShareGroupPrivateKeyRequest(device_ids); + + FailShareGroupPrivateKeyRequest(NetworkRequestError::kBadRequest); + + VerifyShareGroupPrivateKeyResult( + CryptAuthDeviceSyncResult::ResultCode:: + kErrorShareGroupPrivateKeyApiCallBadRequest); +} + +} // namespace device_sync + +} // namespace chromeos
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn index 6d0474ad..b8b2b203 100644 --- a/components/arc/BUILD.gn +++ b/components/arc/BUILD.gn
@@ -42,6 +42,8 @@ "intent_helper/link_handler_model.cc", "intent_helper/link_handler_model.h", "intent_helper/open_url_delegate.h", + "keymaster/arc_keymaster_bridge.cc", + "keymaster/arc_keymaster_bridge.h", "lock_screen/arc_lock_screen_bridge.cc", "lock_screen/arc_lock_screen_bridge.h", "metrics/arc_metrics_service.cc",
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn index f01f7f88..c8064bb 100644 --- a/components/arc/common/BUILD.gn +++ b/components/arc/common/BUILD.gn
@@ -33,6 +33,7 @@ "input_method_manager.mojom", "intent_common.mojom", "intent_helper.mojom", + "keymaster.mojom", "kiosk.mojom", "lock_screen.mojom", "media_session.mojom",
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom index 137f320..491cdc2 100644 --- a/components/arc/common/arc_bridge.mojom +++ b/components/arc/common/arc_bridge.mojom
@@ -24,6 +24,7 @@ import "components/arc/common/ime.mojom"; import "components/arc/common/input_method_manager.mojom"; import "components/arc/common/intent_helper.mojom"; +import "components/arc/common/keymaster.mojom"; import "components/arc/common/kiosk.mojom"; import "components/arc/common/lock_screen.mojom"; import "components/arc/common/media_session.mojom"; @@ -54,9 +55,9 @@ import "components/arc/common/wake_lock.mojom"; import "components/arc/common/wallpaper.mojom"; -// Next MinVersion: 47 +// Next MinVersion: 48 // Deprecated method IDs: 101, 105 -// Next method ID: 152 +// Next method ID: 153 interface ArcBridgeHost { // Keep the entries alphabetical. In order to do so without breaking // compatibility with the ARC instance, explicitly assign each interface a @@ -131,6 +132,9 @@ [MinVersion=4] OnIntentHelperInstanceReady@111( IntentHelperInstance instance_ptr); + // Notifies Chrome that the KeymasterInstance interface is ready. + [MinVersion=47] OnKeymasterInstanceReady@152(KeymasterInstance instance_ptr); + // Notifies Chrome that the KioskInstance interface is ready. [MinVersion=20] OnKioskInstanceReady@126(KioskInstance instance_ptr);
diff --git a/components/arc/common/keymaster.mojom b/components/arc/common/keymaster.mojom new file mode 100644 index 0000000..24475b72 --- /dev/null +++ b/components/arc/common/keymaster.mojom
@@ -0,0 +1,212 @@ +// 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. + +// Next MinVersion: 1 + +// This file defines the mojo interface between Android and Chrome OS for the +// keymaster implementation used in ARC. + +module arc.mojom; + +// Host is implemented in Chrome. Listens until server and instance come online +// and forwards a server handle to the instance. +interface KeymasterHost { + GetServer@0() => (KeymasterServer server_ptr); +}; + +// Instance is implemented in ARC. Retrieves a server pointer from the host and +// uses it to fulfill Android Keymaster operations. +interface KeymasterInstance { + Init@0(KeymasterHost host_ptr) => (); +}; + +// Server is implemented in arc-keymasterd in Chrome OS. This interface is the +// mojo equivalent of the Keymaster 3.0 HIDL interface. Please refer to +// Android's IKeymasterDevice.hal for a more detailed description on how the +// methods and structs below should function. +interface KeymasterServer { + + // Sets the Android version information used. + SetSystemVersion@0(uint32 os_version, uint32 os_patchlevel); + + AddRngEntropy@1(array<uint8> data) => (int32 error); + + // Returns the characteristics of the specified key if it is valid. + GetKeyCharacteristics@2(GetKeyCharacteristicsRequest request) => + (GetKeyCharacteristicsResult response); + + GenerateKey@3(array<KeyParameter> key_params) => (GenerateKeyResult response); + + ImportKey@4(ImportKeyRequest request) => (ImportKeyResult response); + + // Exports a public key, returning the key in the specified format. + ExportKey@5(ExportKeyRequest request) => (ExportKeyResult response); + + // Generates a signed X.509 certificate chain attesting to the presence of + // keyToAttest in Keymaster. + AttestKey@6(AttestKeyRequest request) => (AttestKeyResult result); + + // Upgrades a key generated by an older version of the Keymaster. + UpgradeKey@7(UpgradeKeyRequest request) => (UpgradeKeyResult response); + + DeleteKey@8(array<uint8> key_blob) => (int32 error); + + DeleteAllKeys@9() => (int32 error); + + // Begins a cryptographic operation using the specified key. + Begin@10(BeginRequest request) => (BeginResult result); + + // Provides data and possibly receives output from an ongoing operation. + Update@11(UpdateRequest request) => (UpdateResult response); + + // Finalizes a cryptographic operation and invalidates operation handle. + Finish@12(FinishRequest request) => (FinishResult response); + + // Aborts an operation and invalidates the operation handle. + Abort@13(uint64 op_handle) => (int32 error); +}; + +//////////////////////////////////////////////////////////////////////////////// +// KeymasterServer helper enums and structs + +[Extensible] +enum KeyPurpose { + ENCRYPT = 0, // Usable with RSA, EC and AES keys. + DECRYPT = 1, // Usable with RSA, EC and AES keys. + SIGN = 2, // Usable with RSA, EC and HMAC keys. + VERIFY = 3, // Usable with RSA, EC and HMAC keys. + DERIVE_KEY = 4, // Usable with EC keys. + WRAP_KEY = 5, // Usable with wrapping keys. +}; + +[Extensible] +enum KeyFormat { + X509 = 0, // for public key export + PKCS8 = 1, // for asymmetric key pair import + RAW = 3, // for symmetric key import and export +}; + +// Helper union for key parameter values. +union IntegerKeyParam { + bool boolean_value; // KM_BOOL + uint32 integer; // KM_ENUM, KM_ENUM_REP, KM_INT and KM_INT_REP + uint64 long_integer; // KM_LONG + uint64 date_time; // KM_DATE + array<uint8> blob; // KM_BIGNUM and KM_BYTES +}; + +struct KeyParameter { + // Discriminates the IntegerKeyParam union field used. + uint32 tag; + IntegerKeyParam param; +}; + +// Defines the attributes of a key, including cryptographic parameters, and +// usage restrictions. +struct KeyCharacteristics { + array<KeyParameter> software_enforced; + array<KeyParameter> tee_enforced; +}; + +//////////////////////////////////////////////////////////////////////////////// +// KeymasterServer request and response structs + +struct GetKeyCharacteristicsRequest { + array<uint8> key_blob; + array<uint8> client_id; + array<uint8> app_data; +}; + +struct GetKeyCharacteristicsResult { + KeyCharacteristics key_characteristics; + int32 error; +}; + +struct GenerateKeyResult { + array<uint8> key_blob; + KeyCharacteristics key_characteristics; + int32 error; +}; + +struct ImportKeyRequest { + array<KeyParameter> key_description; + KeyFormat key_format; + array<uint8> key_data; +}; + +struct ImportKeyResult { + array<uint8> key_blob; + KeyCharacteristics key_characteristics; + int32 error; +}; + +struct ExportKeyRequest { + KeyFormat key_format; + array<uint8> key_blob; + array<uint8> client_id; + array<uint8> app_data; +}; + +struct ExportKeyResult { + array<uint8> key_material; + int32 error; +}; + +struct AttestKeyRequest { + array<uint8> key_to_attest; + array<KeyParameter> attest_params; +}; + +struct AttestKeyResult { + array<array<uint8>> cert_chain; + int32 error; +}; + +struct UpgradeKeyRequest { + array<uint8> key_blob_to_upgrade; + array<KeyParameter> upgrade_params; +}; + +struct UpgradeKeyResult { + array<uint8> upgraded_key_blob; + int32 error; +}; + +struct BeginRequest { + KeyPurpose purpose; + array<uint8> key; + array<KeyParameter> in_params; +}; + +struct BeginResult { + array<KeyParameter> out_params; + uint64 op_handle; + int32 error; +}; + +struct UpdateRequest { + uint64 op_handle; + array<KeyParameter> in_params; + array<uint8> input; +}; + +struct UpdateResult { + uint32 input_consumed; + array<KeyParameter> out_params; + array<uint8> output; + int32 error; +}; + +struct FinishRequest { + uint64 op_handle; + array<KeyParameter> in_params; + array<uint8> input; + array<uint8> signature; +}; + +struct FinishResult { + array<KeyParameter> out_params; + array<uint8> output; + int32 error; +};
diff --git a/components/arc/keymaster/arc_keymaster_bridge.cc b/components/arc/keymaster/arc_keymaster_bridge.cc new file mode 100644 index 0000000..12d57a2 --- /dev/null +++ b/components/arc/keymaster/arc_keymaster_bridge.cc
@@ -0,0 +1,104 @@ +// 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 "components/arc/keymaster/arc_keymaster_bridge.h" + +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/process/process_handle.h" +#include "chromeos/dbus/arc_keymaster_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "components/arc/arc_browser_context_keyed_service_factory_base.h" +#include "components/arc/session/arc_bridge_service.h" +#include "mojo/public/cpp/platform/platform_channel.h" +#include "mojo/public/cpp/system/invitation.h" + +namespace arc { +namespace { + +// Singleton factory for ArcKeymasterBridge +class ArcKeymasterBridgeFactory + : public internal::ArcBrowserContextKeyedServiceFactoryBase< + ArcKeymasterBridge, + ArcKeymasterBridgeFactory> { + public: + // Factory name used by ArcBrowserContextKeyedServiceFactoryBase. + static constexpr const char* kName = "ArcKeymasterBridgeFactory"; + + static ArcKeymasterBridgeFactory* GetInstance() { + return base::Singleton<ArcKeymasterBridgeFactory>::get(); + } + + private: + friend base::DefaultSingletonTraits<ArcKeymasterBridgeFactory>; + ArcKeymasterBridgeFactory() = default; + ~ArcKeymasterBridgeFactory() override = default; +}; + +} // namespace + +// static +ArcKeymasterBridge* ArcKeymasterBridge::GetForBrowserContext( + content::BrowserContext* context) { + return ArcKeymasterBridgeFactory::GetForBrowserContext(context); +} + +ArcKeymasterBridge::ArcKeymasterBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service) + : arc_bridge_service_(bridge_service), weak_factory_(this) { + arc_bridge_service_->keymaster()->SetHost(this); +} + +ArcKeymasterBridge::~ArcKeymasterBridge() { + arc_bridge_service_->keymaster()->SetHost(nullptr); +} + +void ArcKeymasterBridge::GetServer(GetServerCallback callback) { + if (!keymaster_server_proxy_.is_bound()) { + BootstrapMojoConnection(std::move(callback)); + return; + } + std::move(callback).Run(std::move(keymaster_server_proxy_)); +} + +void ArcKeymasterBridge::OnBootstrapMojoConnection(GetServerCallback callback, + bool result) { + if (!result) { + LOG(ERROR) << "Error bootstrapping Mojo in arc-keymasterd."; + keymaster_server_proxy_.reset(); + std::move(callback).Run(nullptr); + return; + } + DVLOG(1) << "Success bootstrapping Mojo in arc-keymasterd."; + std::move(callback).Run(std::move(keymaster_server_proxy_)); +} + +void ArcKeymasterBridge::BootstrapMojoConnection(GetServerCallback callback) { + DVLOG(1) << "Bootstrapping arc-keymasterd Mojo connection via D-Bus."; + mojo::OutgoingInvitation invitation; + mojo::PlatformChannel channel; + mojo::ScopedMessagePipeHandle server_pipe = + invitation.AttachMessagePipe("arc-keymaster-pipe"); + mojo::OutgoingInvitation::Send(std::move(invitation), + base::kNullProcessHandle, + channel.TakeLocalEndpoint()); + + keymaster_server_proxy_.Bind(mojo::InterfacePtrInfo<mojom::KeymasterServer>( + std::move(server_pipe), 0u)); + DVLOG(1) << "Bound remote KeymasterServer interface to pipe."; + keymaster_server_proxy_.set_connection_error_handler( + base::BindOnce(&mojo::InterfacePtr<mojom::KeymasterServer>::reset, + base::Unretained(&keymaster_server_proxy_))); + chromeos::DBusThreadManager::Get() + ->GetArcKeymasterClient() + ->BootstrapMojoConnection( + channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(), + base::BindOnce(&ArcKeymasterBridge::OnBootstrapMojoConnection, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +} // namespace arc
diff --git a/components/arc/keymaster/arc_keymaster_bridge.h b/components/arc/keymaster/arc_keymaster_bridge.h new file mode 100644 index 0000000..32833e3f --- /dev/null +++ b/components/arc/keymaster/arc_keymaster_bridge.h
@@ -0,0 +1,58 @@ +// 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 COMPONENTS_ARC_KEYMASTER_ARC_KEYMASTER_BRIDGE_H_ +#define COMPONENTS_ARC_KEYMASTER_ARC_KEYMASTER_BRIDGE_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "components/arc/common/keymaster.mojom.h" +#include "components/keyed_service/core/keyed_service.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace arc { + +class ArcBridgeService; + +// This class is responsible for providing a KeymasterServer proxy by +// bootstrapping a mojo connection with the arc-keymasterd daemon. The mojo +// connection is bootstrapped lazily during the first call to GetServer. Chrome +// has no further involvement once the KeymasterServer proxy has been forwarded +// to the KeymasterInstance in ARC. +class ArcKeymasterBridge : public KeyedService, public mojom::KeymasterHost { + public: + // Returns singleton instance for the given BrowserContext, or nullptr if the + // browser |context| is not allowed to use ARC. + static ArcKeymasterBridge* GetForBrowserContext( + content::BrowserContext* context); + + ArcKeymasterBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service); + ~ArcKeymasterBridge() override; + + // KeymasterHost mojo interface. + using mojom::KeymasterHost::GetServerCallback; + void GetServer(GetServerCallback callback) override; + + private: + void BootstrapMojoConnection(GetServerCallback callback); + void OnBootstrapMojoConnection(GetServerCallback callback, bool result); + + ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. + + // Points to a proxy bound to the implementation in arc-keymasterd. + mojom::KeymasterServerPtr keymaster_server_proxy_; + + // WeakPtrFactory to use for callbacks. + base::WeakPtrFactory<ArcKeymasterBridge> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ArcKeymasterBridge); +}; + +} // namespace arc + +#endif // COMPONENTS_ARC_KEYMASTER_ARC_KEYMASTER_BRIDGE_H_
diff --git a/components/arc/session/arc_bridge_host_impl.cc b/components/arc/session/arc_bridge_host_impl.cc index a672d1c..4558486 100644 --- a/components/arc/session/arc_bridge_host_impl.cc +++ b/components/arc/session/arc_bridge_host_impl.cc
@@ -30,6 +30,7 @@ #include "components/arc/common/ime.mojom.h" #include "components/arc/common/input_method_manager.mojom.h" #include "components/arc/common/intent_helper.mojom.h" +#include "components/arc/common/keymaster.mojom.h" #include "components/arc/common/kiosk.mojom.h" #include "components/arc/common/lock_screen.mojom.h" #include "components/arc/common/media_session.mojom.h" @@ -191,6 +192,11 @@ std::move(intent_helper_ptr)); } +void ArcBridgeHostImpl::OnKeymasterInstanceReady( + mojom::KeymasterInstancePtr keymaster_ptr) { + OnInstanceReady(arc_bridge_service_->keymaster(), std::move(keymaster_ptr)); +} + void ArcBridgeHostImpl::OnKioskInstanceReady( mojom::KioskInstancePtr kiosk_ptr) { OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr));
diff --git a/components/arc/session/arc_bridge_host_impl.h b/components/arc/session/arc_bridge_host_impl.h index 043e20a..0f5868dd 100644 --- a/components/arc/session/arc_bridge_host_impl.h +++ b/components/arc/session/arc_bridge_host_impl.h
@@ -70,6 +70,8 @@ mojom::InputMethodManagerInstancePtr input_method_manager_ptr) override; void OnIntentHelperInstanceReady( mojom::IntentHelperInstancePtr intent_helper_ptr) override; + void OnKeymasterInstanceReady( + mojom::KeymasterInstancePtr keymaster_ptr) override; void OnKioskInstanceReady(mojom::KioskInstancePtr kiosk_ptr) override; void OnLockScreenInstanceReady( mojom::LockScreenInstancePtr lock_screen_ptr) override;
diff --git a/components/arc/session/arc_bridge_service.cc b/components/arc/session/arc_bridge_service.cc index 005a5be..485b0fea 100644 --- a/components/arc/session/arc_bridge_service.cc +++ b/components/arc/session/arc_bridge_service.cc
@@ -26,6 +26,7 @@ #include "components/arc/common/ime.mojom.h" #include "components/arc/common/input_method_manager.mojom.h" #include "components/arc/common/intent_helper.mojom.h" +#include "components/arc/common/keymaster.mojom.h" #include "components/arc/common/kiosk.mojom.h" #include "components/arc/common/lock_screen.mojom.h" #include "components/arc/common/media_session.mojom.h"
diff --git a/components/arc/session/arc_bridge_service.h b/components/arc/session/arc_bridge_service.h index 6bb28f7..d3f0f3b4 100644 --- a/components/arc/session/arc_bridge_service.h +++ b/components/arc/session/arc_bridge_service.h
@@ -51,6 +51,8 @@ class InputMethodManagerInstance; class IntentHelperHost; class IntentHelperInstance; +class KeymasterHost; +class KeymasterInstance; class KioskHost; class KioskInstance; class LockScreenInstance; @@ -177,6 +179,10 @@ intent_helper() { return &intent_helper_; } + ConnectionHolder<mojom::KeymasterInstance, mojom::KeymasterHost>* + keymaster() { + return &keymaster_; + } ConnectionHolder<mojom::KioskInstance, mojom::KioskHost>* kiosk() { return &kiosk_; } @@ -281,6 +287,7 @@ input_method_manager_; ConnectionHolder<mojom::IntentHelperInstance, mojom::IntentHelperHost> intent_helper_; + ConnectionHolder<mojom::KeymasterInstance, mojom::KeymasterHost> keymaster_; ConnectionHolder<mojom::KioskInstance, mojom::KioskHost> kiosk_; ConnectionHolder<mojom::LockScreenInstance> lock_screen_; ConnectionHolder<mojom::MediaSessionInstance> media_session_;
diff --git a/components/arc/test/fake_arc_bridge_host.cc b/components/arc/test/fake_arc_bridge_host.cc index 1d600aec..3cc956e1 100644 --- a/components/arc/test/fake_arc_bridge_host.cc +++ b/components/arc/test/fake_arc_bridge_host.cc
@@ -25,6 +25,7 @@ #include "components/arc/common/ime.mojom.h" #include "components/arc/common/input_method_manager.mojom.h" #include "components/arc/common/intent_helper.mojom.h" +#include "components/arc/common/keymaster.mojom.h" #include "components/arc/common/kiosk.mojom.h" #include "components/arc/common/lock_screen.mojom.h" #include "components/arc/common/media_session.mojom.h" @@ -118,6 +119,9 @@ void FakeArcBridgeHost::OnIntentHelperInstanceReady( mojom::IntentHelperInstancePtr intent_helper_ptr) {} +void FakeArcBridgeHost::OnKeymasterInstanceReady( + mojom::KeymasterInstancePtr keymaster_ptr) {} + void FakeArcBridgeHost::OnKioskInstanceReady( mojom::KioskInstancePtr kiosk_ptr) {}
diff --git a/components/arc/test/fake_arc_bridge_host.h b/components/arc/test/fake_arc_bridge_host.h index 2d94ddf..4bb07b26 100644 --- a/components/arc/test/fake_arc_bridge_host.h +++ b/components/arc/test/fake_arc_bridge_host.h
@@ -50,6 +50,8 @@ mojom::InputMethodManagerInstancePtr input_method_manager_ptr) override; void OnIntentHelperInstanceReady( mojom::IntentHelperInstancePtr intent_helper_ptr) override; + void OnKeymasterInstanceReady( + mojom::KeymasterInstancePtr keymaster_ptr) override; void OnKioskInstanceReady(mojom::KioskInstancePtr kiosk_ptr) override; void OnLockScreenInstanceReady( mojom::LockScreenInstancePtr lock_screen_ptr) override;
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc index e86b1ea..c3a047a 100644 --- a/components/autofill/content/browser/content_autofill_driver.cc +++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -82,6 +82,10 @@ return render_frame_host_->GetParent() == nullptr; } +ui::AXTreeID ContentAutofillDriver::GetAxTreeId() const { + return render_frame_host_->GetAXTreeID(); +} + net::URLRequestContextGetter* ContentAutofillDriver::GetURLRequestContext() { return content::BrowserContext::GetDefaultStoragePartition( render_frame_host_->GetSiteInstance()->GetBrowserContext())->
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h index a607978..658cb4c2 100644 --- a/components/autofill/content/browser/content_autofill_driver.h +++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -55,6 +55,7 @@ // AutofillDriver: bool IsIncognito() const override; bool IsInMainFrame() const override; + ui::AXTreeID GetAxTreeId() const override; net::URLRequestContextGetter* GetURLRequestContext() override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; bool RendererIsAvailable() override;
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc index 9592494d..c33dba2f 100644 --- a/components/autofill/content/renderer/form_autofill_util.cc +++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1679,6 +1679,7 @@ field->id_attribute = element.GetAttribute(*kId).Utf16(); field->name_attribute = element.GetAttribute(*kName).Utf16(); field->unique_renderer_id = element.UniqueRendererFormControlId(); + field->form_control_ax_id = element.GetAxId(); field->form_control_type = element.FormControlTypeForAutofill().Utf8(); field->autocomplete_attribute = element.GetAttribute(*kAutocomplete).Utf8(); if (field->autocomplete_attribute.size() > kMaxDataLength) {
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 2ddca8c..9f72809 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -484,6 +484,7 @@ "//testing/gtest", "//third_party/libaddressinput:test_support", "//third_party/libaddressinput:util", + "//ui/accessibility", "//ui/gfx:test_support", "//ui/gfx/geometry", ]
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h index dca5305..47aa24b 100644 --- a/components/autofill/core/browser/autofill_driver.h +++ b/components/autofill/core/browser/autofill_driver.h
@@ -27,6 +27,10 @@ class RectF; } +namespace ui { +class AXTreeID; +} + namespace autofill { class FormStructure; @@ -52,6 +56,9 @@ // Returns whether AutofillDriver instance is associated to the main frame. virtual bool IsInMainFrame() const = 0; + // Returns the ax tree id associated with this driver. + virtual ui::AXTreeID GetAxTreeId() const = 0; + // Returns the URL request context information associated with this driver. virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc index de417bf8..fa8aeb0 100644 --- a/components/autofill/core/browser/autofill_external_delegate.cc +++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -304,6 +304,10 @@ manager_->client()->HideAutofillPopup(); } +int32_t AutofillExternalDelegate::GetWebContentsPopupControllerAxId() const { + return query_field_.form_control_ax_id; +} + base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h index 9de5b4a3..2783c46 100644 --- a/components/autofill/core/browser/autofill_external_delegate.h +++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -96,6 +96,10 @@ // values or settings. void Reset(); + // Returns the ax node id associated with the current web contents' element + // who has a controller relation to the current autofill popup. + int32_t GetWebContentsPopupControllerAxId() const; + protected: base::WeakPtr<AutofillExternalDelegate> GetWeakPtr();
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc index 92426282..05abf3a 100644 --- a/components/autofill/core/browser/test_autofill_driver.cc +++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -9,6 +9,7 @@ #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/gfx/geometry/rect_f.h" namespace autofill { @@ -29,6 +30,11 @@ return is_in_main_frame_; } +ui::AXTreeID TestAutofillDriver::GetAxTreeId() const { + NOTIMPLEMENTED() << "See https://crbug.com/985933"; + return ui::AXTreeIDUnknown(); +} + net::URLRequestContextGetter* TestAutofillDriver::GetURLRequestContext() { return url_request_context_; }
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h index 86fbd4e0..160f561 100644 --- a/components/autofill/core/browser/test_autofill_driver.h +++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -23,6 +23,7 @@ // AutofillDriver implementation overrides. bool IsIncognito() const override; bool IsInMainFrame() const override; + ui::AXTreeID GetAxTreeId() const override; // Returns the value passed in to the last call to |SetURLRequestContext()| // or NULL if that method has never been called. net::URLRequestContextGetter* GetURLRequestContext() override;
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc index d07e918..b8c0f13c 100644 --- a/components/autofill/core/common/form_field_data.cc +++ b/components/autofill/core/common/form_field_data.cc
@@ -207,6 +207,7 @@ bool FormFieldData::operator==(const FormFieldData& field) const { return SameFieldAs(field) && unique_renderer_id == field.unique_renderer_id && + form_control_ax_id == field.form_control_ax_id && is_autofilled == field.is_autofilled && check_status == field.check_status && option_values == field.option_values &&
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h index 3727d963..0fa56be 100644 --- a/components/autofill/core/common/form_field_data.h +++ b/components/autofill/core/common/form_field_data.h
@@ -134,6 +134,9 @@ // SameFieldAs(). uint32_t unique_renderer_id = kNotSetFormControlRendererId; + // The ax node id of the form control in the accessibility tree. + int32_t form_control_ax_id = 0; + // The unique identifier of the section (e.g. billing vs. shipping address) // of this field. std::string section;
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom index bb1f12fc..503138cd 100644 --- a/components/autofill/core/common/mojom/autofill_types.mojom +++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -120,6 +120,7 @@ mojo_base.mojom.String16 aria_description; uint32 unique_renderer_id; uint32 properties_mask; + int32 form_control_ax_id; uint64 max_length; bool is_autofilled;
diff --git a/components/autofill/core/common/mojom/autofill_types_struct_traits.cc b/components/autofill/core/common/mojom/autofill_types_struct_traits.cc index ec05eab..657ce2e 100644 --- a/components/autofill/core/common/mojom/autofill_types_struct_traits.cc +++ b/components/autofill/core/common/mojom/autofill_types_struct_traits.cc
@@ -51,6 +51,7 @@ out->properties_mask = data.properties_mask(); out->unique_renderer_id = data.unique_renderer_id(); + out->form_control_ax_id = data.form_control_ax_id(); out->max_length = data.max_length(); out->is_autofilled = data.is_autofilled();
diff --git a/components/autofill/core/common/mojom/autofill_types_struct_traits.h b/components/autofill/core/common/mojom/autofill_types_struct_traits.h index 6345e04e..68a96f8aa 100644 --- a/components/autofill/core/common/mojom/autofill_types_struct_traits.h +++ b/components/autofill/core/common/mojom/autofill_types_struct_traits.h
@@ -88,6 +88,10 @@ return r.properties_mask; } + static int32_t form_control_ax_id(const autofill::FormFieldData& r) { + return r.form_control_ax_id; + } + static uint64_t max_length(const autofill::FormFieldData& r) { return r.max_length; }
diff --git a/components/autofill/ios/browser/BUILD.gn b/components/autofill/ios/browser/BUILD.gn index 21b5d47..67144b8 100644 --- a/components/autofill/ios/browser/BUILD.gn +++ b/components/autofill/ios/browser/BUILD.gn
@@ -46,6 +46,7 @@ "//ios/web/public/js_messaging", "//ios/web/public/security", "//services/network/public/cpp", + "//ui/accessibility", "//ui/gfx/geometry", ] }
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h index 3c73515..326dcaa 100644 --- a/components/autofill/ios/browser/autofill_driver_ios.h +++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -40,6 +40,7 @@ // AutofillDriver: bool IsIncognito() const override; bool IsInMainFrame() const override; + ui::AXTreeID GetAxTreeId() const override; net::URLRequestContextGetter* GetURLRequestContext() override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; bool RendererIsAvailable() override;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm index 0741b18..718de85 100644 --- a/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -14,6 +14,7 @@ #import "ios/web/public/web_state/web_state.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/gfx/geometry/rect_f.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -71,6 +72,11 @@ return web_frame ? web_frame->IsMainFrame() : true; } +ui::AXTreeID AutofillDriverIOS::GetAxTreeId() const { + NOTIMPLEMENTED() << "See https://crbug.com/985933"; + return ui::AXTreeIDUnknown(); +} + net::URLRequestContextGetter* AutofillDriverIOS::GetURLRequestContext() { return web_state_->GetBrowserState()->GetRequestContext(); }
diff --git a/components/nacl/browser/nacl_browser_delegate.h b/components/nacl/browser/nacl_browser_delegate.h index b0894e2..9d73cf0 100644 --- a/components/nacl/browser/nacl_browser_delegate.h +++ b/components/nacl/browser/nacl_browser_delegate.h
@@ -22,10 +22,6 @@ } } -namespace extensions { -class ExtensionSystem; -} - // Encapsulates the dependencies of NaCl code on chrome/, to avoid a direct // dependency on chrome/. class NaClBrowserDelegate { @@ -67,11 +63,10 @@ // |use_blocking_api| to true, so calling blocking file API is allowed // otherwise non blocking API will be used (which only handles a subset of the // urls checking only the url scheme against kExtensionScheme). - virtual bool MapUrlToLocalFilePath( - const GURL& url, - bool use_blocking_api, - extensions::ExtensionSystem* extension_system, - base::FilePath* file_path) = 0; + virtual bool MapUrlToLocalFilePath(const GURL& url, + bool use_blocking_api, + const base::FilePath& profile_directory, + base::FilePath* file_path) = 0; // Set match patterns which will be checked before enabling debug stub. virtual void SetDebugPatterns(const std::string& debug_patterns) = 0; @@ -79,13 +74,8 @@ virtual bool URLMatchesDebugPatterns(const GURL& manifest_url) = 0; // Returns whether Non-SFI mode is allowed for a given manifest URL. - virtual bool IsNonSfiModeAllowed( - extensions::ExtensionSystem* extension_system, - const GURL& manifest_url) = 0; - // Returns a pointer to the extension system. Must be called on the UI thread, - // but the resulting pointer can be used on the IO thread. - virtual extensions::ExtensionSystem* GetExtensionSystem( - const base::FilePath& profile_directory) = 0; + virtual bool IsNonSfiModeAllowed(const base::FilePath& profile_directory, + const GURL& manifest_url) = 0; }; #endif // COMPONENTS_NACL_BROWSER_NACL_BROWSER_DELEGATE_H_
diff --git a/components/nacl/browser/nacl_file_host.cc b/components/nacl/browser/nacl_file_host.cc index af8009a..6d76b7e 100644 --- a/components/nacl/browser/nacl_file_host.cc +++ b/components/nacl/browser/nacl_file_host.cc
@@ -129,8 +129,10 @@ IPC::Message* reply_msg) { base::FilePath file_path; if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath( - file_url, true /* use_blocking_api */, - nacl_host_message_filter->extension_system(), &file_path)) { + file_url, + true /* use_blocking_api */, + nacl_host_message_filter->profile_directory(), + &file_path)) { NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg); return; }
diff --git a/components/nacl/browser/nacl_host_message_filter.cc b/components/nacl/browser/nacl_host_message_filter.cc index c4eaaf2..05dee81 100644 --- a/components/nacl/browser/nacl_host_message_filter.cc +++ b/components/nacl/browser/nacl_host_message_filter.cc
@@ -87,18 +87,6 @@ NaClHostMessageFilter::~NaClHostMessageFilter() { } -void NaClHostMessageFilter::OnFilterAdded(IPC::Channel* channel) { - BrowserMessageFilter::OnFilterAdded(channel); - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&NaClHostMessageFilter::SetUpExtensionSystem, this)); -} - -void NaClHostMessageFilter::SetUpExtensionSystem() { - extension_system_ = - nacl::NaClBrowser::GetDelegate()->GetExtensionSystem(profile_directory_); -} - void NaClHostMessageFilter::OnChannelClosing() { pnacl::PnaclHost::GetInstance()->RendererClosing(render_process_id_); } @@ -214,7 +202,8 @@ if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath( gurl, true, // use_blocking_api - extension_system_, &file_path_metadata)) { + profile_directory_, + &file_path_metadata)) { continue; } base::File file = nacl::OpenNaClReadExecImpl( @@ -270,9 +259,10 @@ // because we're running in the I/O thread. Ideally we'd use the other path, // which would cover more cases. nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath( - manifest_url, false /* use_blocking_api */, extension_system_, + manifest_url, + false /* use_blocking_api */, + profile_directory_, &manifest_path); - host->Launch(this, reply_msg, manifest_path); }
diff --git a/components/nacl/browser/nacl_host_message_filter.h b/components/nacl/browser/nacl_host_message_filter.h index 3f472d7..474ac45 100644 --- a/components/nacl/browser/nacl_host_message_filter.h +++ b/components/nacl/browser/nacl_host_message_filter.h
@@ -11,7 +11,6 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "components/nacl/browser/nacl_browser_delegate.h" #include "content/public/browser/browser_message_filter.h" #include "ppapi/shared_impl/ppapi_permissions.h" @@ -36,14 +35,10 @@ // content::BrowserMessageFilter methods: bool OnMessageReceived(const IPC::Message& message) override; void OnChannelClosing() override; - void OnFilterAdded(IPC::Channel* channel) override; int render_process_id() { return render_process_id_; } bool off_the_record() { return off_the_record_; } const base::FilePath& profile_directory() const { return profile_directory_; } - extensions::ExtensionSystem* extension_system() const { - return extension_system_; - } private: friend class content::BrowserThread; @@ -84,7 +79,6 @@ const base::File& file, bool is_hit); void OnNaClDebugEnabledForURL(const GURL& nmf_url, bool* should_debug); - void SetUpExtensionSystem(); int render_process_id_; @@ -92,9 +86,6 @@ // read on the IO thread. bool off_the_record_; base::FilePath profile_directory_; - // The extension system must be fetched on the UI thread, but the extension - // info map can be used on the IO thread. - extensions::ExtensionSystem* extension_system_; base::WeakPtrFactory<NaClHostMessageFilter> weak_ptr_factory_{this};
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc index 39c6744..6169ba9a 100644 --- a/components/nacl/browser/nacl_process_host.cc +++ b/components/nacl/browser/nacl_process_host.cc
@@ -383,7 +383,7 @@ #if defined(OS_CHROMEOS) && \ (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)) nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed( - nacl_host_message_filter->extension_system(), manifest_url_); + nacl_host_message_filter->profile_directory(), manifest_url_); #endif #endif bool nonsfi_mode_enabled =
diff --git a/components/nacl/browser/test_nacl_browser_delegate.cc b/components/nacl/browser/test_nacl_browser_delegate.cc index 64595eee..81e925c1 100644 --- a/components/nacl/browser/test_nacl_browser_delegate.cc +++ b/components/nacl/browser/test_nacl_browser_delegate.cc
@@ -44,7 +44,7 @@ bool TestNaClBrowserDelegate::MapUrlToLocalFilePath( const GURL& url, bool use_blocking_api, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) { return false; } @@ -58,12 +58,7 @@ } bool TestNaClBrowserDelegate::IsNonSfiModeAllowed( - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, const GURL& manifest_url) { return false; } - -extensions::ExtensionSystem* TestNaClBrowserDelegate::GetExtensionSystem( - const base::FilePath& profile_directory) { - return nullptr; -}
diff --git a/components/nacl/browser/test_nacl_browser_delegate.h b/components/nacl/browser/test_nacl_browser_delegate.h index 75ca44a..69dde4e 100644 --- a/components/nacl/browser/test_nacl_browser_delegate.h +++ b/components/nacl/browser/test_nacl_browser_delegate.h
@@ -34,14 +34,12 @@ content::BrowserPpapiHost* ppapi_host) override; bool MapUrlToLocalFilePath(const GURL& url, bool use_blocking_api, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) override; void SetDebugPatterns(const std::string& debug_patterns) override; bool URLMatchesDebugPatterns(const GURL& manifest_url) override; - bool IsNonSfiModeAllowed(extensions::ExtensionSystem* extension_system, + bool IsNonSfiModeAllowed(const base::FilePath& profile_directory, const GURL& manifest_url) override; - extensions::ExtensionSystem* GetExtensionSystem( - const base::FilePath& profile_directory) override; private: DISALLOW_COPY_AND_ASSIGN(TestNaClBrowserDelegate);
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc index 66f1e01..f91d8ab 100644 --- a/components/omnibox/browser/omnibox_edit_model.cc +++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -572,12 +572,6 @@ match_selection_timestamp); } -bool OmniboxEditModel::ClassifiesAsSearch(const base::string16& text) const { - AutocompleteMatch match; - ClassifyString(text, &match, nullptr); - return AutocompleteMatch::IsSearchType(match.type); -} - void OmniboxEditModel::AcceptInput(WindowOpenDisposition disposition, base::TimeTicks match_selection_timestamp) { // Get the URL and transition type for the selected entry.
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h index 594ccfc4..c0753394 100644 --- a/components/omnibox/browser/omnibox_edit_model.h +++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -189,8 +189,11 @@ const base::string16& text, base::TimeTicks match_selection_timestamp = base::TimeTicks()); - // Returns true if |text| classifies as a Search rather than a URL. - bool ClassifiesAsSearch(const base::string16& text) const; + // Sets |match| and |alternate_nav_url| based on classifying |text|. + // |alternate_nav_url| may be nullptr. + void ClassifyString(const base::string16& text, + AutocompleteMatch* match, + GURL* alternate_nav_url) const; // Asks the browser to load the popup's currently selected item, using the // supplied disposition. This may close the popup. @@ -440,12 +443,6 @@ // keyword. static bool IsSpaceCharForAcceptingKeyword(wchar_t c); - // Sets |match| and |alternate_nav_url| based on classifying |text|. - // |alternate_nav_url| may be NULL. - void ClassifyString(const base::string16& text, - AutocompleteMatch* match, - GURL* alternate_nav_url) const; - // Sets the state of user_input_in_progress_. Returns whether said state // changed, so that the caller can evoke NotifyObserversInputInProgress(). bool SetInputInProgressNoNotify(bool in_progress);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index ab50ea6..7d64d1905 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -16228,7 +16228,8 @@ }, }, 'id': 572, - 'supported_on': ['chrome_os:77-'], + 'supported_on': ['chrome_os:78-'], + 'future': True, 'features': { 'dynamic_refresh': True, 'per_profile': True, @@ -16250,7 +16251,7 @@ When this policy is set to correct value, devices will try to query specified print servers for available printers using IPP protocol. - If this policy is unset or set to incorrect value, no server printers are visible by users. + If this policy is unset or set to incorrect value, no server printers are visible to users. Currently, the number of print servers is limited to 16. Only the first 16 print servers from the list will be queried. ''',
diff --git a/components/services/leveldb/BUILD.gn b/components/services/leveldb/BUILD.gn index aff1b0d..d78c2b6 100644 --- a/components/services/leveldb/BUILD.gn +++ b/components/services/leveldb/BUILD.gn
@@ -20,7 +20,7 @@ public_deps = [ "//components/services/filesystem/public/mojom", "//components/services/leveldb/public/cpp", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", ] deps = [ @@ -39,7 +39,7 @@ deps = [ ":lib", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//services/service_manager/public/cpp", @@ -60,7 +60,7 @@ "//components/services/filesystem/public/mojom", "//components/services/leveldb/public/cpp", "//components/services/leveldb/public/cpp:manifest", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//mojo/core/test:run_all_unittests", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system",
diff --git a/components/services/leveldb/OWNERS b/components/services/leveldb/OWNERS index f1bfd1e..ecf8616c 100644 --- a/components/services/leveldb/OWNERS +++ b/components/services/leveldb/OWNERS
@@ -1,7 +1,7 @@ mek@chromium.org -per-file *_struct_traits*.*=set noparent -per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS +per-file *_mojom_traits*.*=set noparent +per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS per-file *.typemap=set noparent per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/components/services/leveldb/leveldb.typemap b/components/services/leveldb/leveldb.typemap index 4b95769..1af5cdfe 100644 --- a/components/services/leveldb/leveldb.typemap +++ b/components/services/leveldb/leveldb.typemap
@@ -2,11 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -mojom = "//components/services/leveldb/public/interfaces/leveldb.mojom" +mojom = "//components/services/leveldb/public/mojom/leveldb.mojom" public_headers = [ "//third_party/leveldatabase/env_chromium.h" ] -traits_headers = [ "//components/services/leveldb/leveldb_struct_traits.h" ] +traits_headers = [ "//components/services/leveldb/leveldb_mojom_traits.h" ] sources = [ - "//components/services/leveldb/leveldb_struct_traits.cc", + "//components/services/leveldb/leveldb_mojom_traits.cc", ] deps = [] public_deps = [
diff --git a/components/services/leveldb/leveldb_app.h b/components/services/leveldb/leveldb_app.h index 0190f38..53b3854bd 100644 --- a/components/services/leveldb/leveldb_app.h +++ b/components/services/leveldb/leveldb_app.h
@@ -7,7 +7,7 @@ #include <memory> -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/service.h"
diff --git a/components/services/leveldb/leveldb_database_impl.h b/components/services/leveldb/leveldb_database_impl.h index 1bcd9e54..aff55f0 100644 --- a/components/services/leveldb/leveldb_database_impl.h +++ b/components/services/leveldb/leveldb_database_impl.h
@@ -11,7 +11,7 @@ #include "base/trace_event/memory_dump_provider.h" #include "base/unguessable_token.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "third_party/leveldatabase/src/include/leveldb/cache.h" #include "third_party/leveldatabase/src/include/leveldb/db.h"
diff --git a/components/services/leveldb/leveldb_mojo_unittest.cc b/components/services/leveldb/leveldb_mojo_unittest.cc index a9cf1f19..8a1efefc 100644 --- a/components/services/leveldb/leveldb_mojo_unittest.cc +++ b/components/services/leveldb/leveldb_mojo_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -#include "components/services/leveldb/leveldb_struct_traits.h" +#include "components/services/leveldb/leveldb_mojom_traits.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/leveldatabase/leveldb_chrome.h"
diff --git a/components/services/leveldb/leveldb_struct_traits.cc b/components/services/leveldb/leveldb_mojom_traits.cc similarity index 95% rename from components/services/leveldb/leveldb_struct_traits.cc rename to components/services/leveldb/leveldb_mojom_traits.cc index bf720e6..0403c35 100644 --- a/components/services/leveldb/leveldb_struct_traits.cc +++ b/components/services/leveldb/leveldb_mojom_traits.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/services/leveldb/leveldb_struct_traits.h" +#include "components/services/leveldb/leveldb_mojom_traits.h" #include "third_party/leveldatabase/env_chromium.h" #include "third_party/leveldatabase/leveldb_chrome.h" @@ -41,7 +41,7 @@ shared_block_read_cache(const leveldb_env::Options& options) { // The Mojo wrapper for leveldb only supports using one of two different // shared caches. Chrome's Mojo wrapper does not currently support custom - // caches, nor NULL to have leveldb create the block read cache. + // caches, nor nullptr to have leveldb create the block read cache. if (!options.block_cache) { // Specify either Default or Web. NOTREACHED();
diff --git a/components/services/leveldb/leveldb_struct_traits.h b/components/services/leveldb/leveldb_mojom_traits.h similarity index 77% rename from components/services/leveldb/leveldb_struct_traits.h rename to components/services/leveldb/leveldb_mojom_traits.h index 2d78836..2bbfafd 100644 --- a/components/services/leveldb/leveldb_struct_traits.h +++ b/components/services/leveldb/leveldb_mojom_traits.h
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SERVICES_LEVELDB_LEVELDB_STRUCT_TRAITS_H_ -#define COMPONENTS_SERVICES_LEVELDB_LEVELDB_STRUCT_TRAITS_H_ +#ifndef COMPONENTS_SERVICES_LEVELDB_LEVELDB_MOJOM_TRAITS_H_ +#define COMPONENTS_SERVICES_LEVELDB_LEVELDB_MOJOM_TRAITS_H_ -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" namespace mojo { @@ -24,4 +24,4 @@ } // namespace mojo -#endif // COMPONENTS_SERVICES_LEVELDB_LEVELDB_STRUCT_TRAITS_H_ +#endif // COMPONENTS_SERVICES_LEVELDB_LEVELDB_MOJOM_TRAITS_H_
diff --git a/components/services/leveldb/leveldb_service_impl.h b/components/services/leveldb/leveldb_service_impl.h index 9deb661..1755d01 100644 --- a/components/services/leveldb/leveldb_service_impl.h +++ b/components/services/leveldb/leveldb_service_impl.h
@@ -7,7 +7,7 @@ #include "base/memory/ref_counted.h" #include "components/services/leveldb/leveldb_mojo_proxy.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" namespace base {
diff --git a/components/services/leveldb/leveldb_service_unittest.cc b/components/services/leveldb/leveldb_service_unittest.cc index 8214ff4..a719d99 100644 --- a/components/services/leveldb/leveldb_service_unittest.cc +++ b/components/services/leveldb/leveldb_service_unittest.cc
@@ -13,7 +13,7 @@ #include "components/services/filesystem/public/mojom/types.mojom.h" #include "components/services/leveldb/public/cpp/manifest.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/service_manager/public/cpp/manifest_builder.h" #include "services/service_manager/public/cpp/test/test_service.h"
diff --git a/components/services/leveldb/public/cpp/BUILD.gn b/components/services/leveldb/public/cpp/BUILD.gn index af0830e5..86ba2cba 100644 --- a/components/services/leveldb/public/cpp/BUILD.gn +++ b/components/services/leveldb/public/cpp/BUILD.gn
@@ -12,7 +12,7 @@ deps = [ "//base", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//services/service_manager/public/cpp", "//third_party/leveldatabase", ] @@ -26,7 +26,7 @@ deps = [ "//base", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//services/service_manager/public/cpp", ] }
diff --git a/components/services/leveldb/public/cpp/manifest.cc b/components/services/leveldb/public/cpp/manifest.cc index 5a563e8..255564c 100644 --- a/components/services/leveldb/public/cpp/manifest.cc +++ b/components/services/leveldb/public/cpp/manifest.cc
@@ -5,7 +5,7 @@ #include "components/services/leveldb/public/cpp/manifest.h" #include "base/no_destructor.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "services/service_manager/public/cpp/manifest_builder.h" namespace leveldb {
diff --git a/components/services/leveldb/public/cpp/remote_iterator.h b/components/services/leveldb/public/cpp/remote_iterator.h index 723e608..cad8da8b 100644 --- a/components/services/leveldb/public/cpp/remote_iterator.h +++ b/components/services/leveldb/public/cpp/remote_iterator.h
@@ -6,7 +6,7 @@ #define COMPONENTS_SERVICES_LEVELDB_PUBLIC_CPP_REMOTE_ITERATOR_H_ #include "base/unguessable_token.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "third_party/leveldatabase/src/include/leveldb/iterator.h" namespace leveldb {
diff --git a/components/services/leveldb/public/cpp/util.h b/components/services/leveldb/public/cpp/util.h index 1986f37..b45b2fa 100644 --- a/components/services/leveldb/public/cpp/util.h +++ b/components/services/leveldb/public/cpp/util.h
@@ -9,7 +9,7 @@ #include <vector> #include "base/strings/string16.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "third_party/leveldatabase/env_chromium.h" namespace leveldb {
diff --git a/components/services/leveldb/public/interfaces/BUILD.gn b/components/services/leveldb/public/mojom/BUILD.gn similarity index 94% rename from components/services/leveldb/public/interfaces/BUILD.gn rename to components/services/leveldb/public/mojom/BUILD.gn index 896343d2..79572d0f 100644 --- a/components/services/leveldb/public/interfaces/BUILD.gn +++ b/components/services/leveldb/public/mojom/BUILD.gn
@@ -4,7 +4,7 @@ import("//mojo/public/tools/bindings/mojom.gni") -mojom("interfaces") { +mojom("mojom") { support_lazy_serialization = true sources = [
diff --git a/components/services/leveldb/public/interfaces/OWNERS b/components/services/leveldb/public/mojom/OWNERS similarity index 100% rename from components/services/leveldb/public/interfaces/OWNERS rename to components/services/leveldb/public/mojom/OWNERS
diff --git a/components/services/leveldb/public/interfaces/leveldb.mojom b/components/services/leveldb/public/mojom/leveldb.mojom similarity index 100% rename from components/services/leveldb/public/interfaces/leveldb.mojom rename to components/services/leveldb/public/mojom/leveldb.mojom
diff --git a/components/services/leveldb/remote_iterator_unittest.cc b/components/services/leveldb/remote_iterator_unittest.cc index 4e482fe..b8077f94 100644 --- a/components/services/leveldb/remote_iterator_unittest.cc +++ b/components/services/leveldb/remote_iterator_unittest.cc
@@ -11,7 +11,7 @@ #include "components/services/leveldb/public/cpp/manifest.h" #include "components/services/leveldb/public/cpp/remote_iterator.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "services/service_manager/public/cpp/manifest_builder.h" #include "services/service_manager/public/cpp/test/test_service.h" #include "services/service_manager/public/cpp/test/test_service_manager.h"
diff --git a/components/ukm/content/source_url_recorder.cc b/components/ukm/content/source_url_recorder.cc index 7b24ecf..7c5facc 100644 --- a/components/ukm/content/source_url_recorder.cc +++ b/components/ukm/content/source_url_recorder.cc
@@ -73,8 +73,10 @@ ui::PageTransition transition, bool started_from_context_menu, bool renderer_initiated) override; + void WebContentsDestroyed() override; ukm::SourceId GetLastCommittedSourceId() const; + ukm::SourceId GetLastCommittedFullNavigationOrSameDocumentSourceId() const; // blink::mojom::UkmSourceIdFrameHost void SetDocumentSourceId(int64_t source_id) override; @@ -97,7 +99,7 @@ void MaybeRecordUrl(content::NavigationHandle* navigation_handle, const GURL& initial_url); - // Recieves document source IDs from the renderer. + // Receives document source IDs from the renderer. content::WebContentsFrameBindingSet<blink::mojom::UkmSourceIdFrameHost> bindings_; @@ -186,6 +188,17 @@ return; } + // Inform the UKM recorder that the previous source is no longer needed to + // be kept alive in memory since we had navigated away. In case of same- + // document navigation, a new source id would have been created similarly to + // full-navigation, thus we are marking the last committed source id + // regardless of which case it came from. + ukm::DelegatingUkmRecorder* ukm_recorder = ukm::DelegatingUkmRecorder::Get(); + if (ukm_recorder) { + ukm_recorder->MarkSourceForDeletion( + GetLastCommittedFullNavigationOrSameDocumentSourceId()); + } + if (navigation_handle->IsSameDocument()) { DCHECK(it == pending_navigations_.end()); HandleSameDocumentNavigation(navigation_handle); @@ -266,11 +279,29 @@ new_recorder->opener_source_id_ = GetLastCommittedSourceId(); } +void SourceUrlRecorderWebContentsObserver::WebContentsDestroyed() { + // Inform the UKM recorder that the previous source is no longer needed to + // be kept alive in memory since the tab has been closed or discarded. In case + // of same-document navigation, a new source id would have been created + // similarly to full-navigation, thus we are marking the last committed source + // id regardless of which case it came from. + ukm::DelegatingUkmRecorder* ukm_recorder = ukm::DelegatingUkmRecorder::Get(); + if (ukm_recorder) { + ukm_recorder->MarkSourceForDeletion( + GetLastCommittedFullNavigationOrSameDocumentSourceId()); + } +} + ukm::SourceId SourceUrlRecorderWebContentsObserver::GetLastCommittedSourceId() const { return last_committed_full_navigation_source_id_; } +ukm::SourceId SourceUrlRecorderWebContentsObserver:: + GetLastCommittedFullNavigationOrSameDocumentSourceId() const { + return last_committed_full_navigation_or_same_document_source_id_; +} + void SourceUrlRecorderWebContentsObserver::SetDocumentSourceId( int64_t source_id) { content::RenderFrameHost* main_frame = web_contents()->GetMainFrame();
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc index dbc55381..4e265f9 100644 --- a/components/ukm/ukm_recorder_impl.cc +++ b/components/ukm/ukm_recorder_impl.cc
@@ -64,8 +64,8 @@ kUkmFeature, "MaxSources", kDefaultMaxSources)); } -// Gets the maximum number of unreferenced Sources kept after purging sources -// that were added to the log. +// Gets the maximum number of Sources we can kept in memory to defer to the next +// reporting interval at the end of the current reporting cycle. size_t GetMaxKeptSources() { constexpr size_t kDefaultMaxKeptSources = 100; return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt( @@ -258,6 +258,12 @@ recording_is_continuous_ = false; } +void UkmRecorderImpl::MarkSourceForDeletion(SourceId source_id) { + if (source_id == kInvalidSourceId) + return; + recordings_.obsolete_source_ids.insert(source_id); +} + void UkmRecorderImpl::SetIsWebstoreExtensionCallback( const IsWebstoreExtensionCallback& callback) { is_webstore_extension_callback_ = callback; @@ -266,23 +272,35 @@ void UkmRecorderImpl::StoreRecordingsInReport(Report* report) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::set<SourceId> ids_seen; + // Set of source ids seen by entries in recordings_. + std::set<SourceId> source_ids_seen; for (const auto& entry : recordings_.entries) { Entry* proto_entry = report->add_entries(); StoreEntryProto(*entry, proto_entry); - ids_seen.insert(entry->source_id); + source_ids_seen.insert(entry->source_id); } + // Number of sources excluded from this report because no entries referred to + // them. + const int num_sources_unsent = + recordings_.sources.size() - source_ids_seen.size(); + // Construct set of whitelisted URLs by merging those carried over from the + // previous report cycle and those from sources recorded in this cycle. std::unordered_set<std::string> url_whitelist; recordings_.carryover_urls_whitelist.swap(url_whitelist); AppendWhitelistedUrls(recordings_.sources, &url_whitelist); - std::vector<std::unique_ptr<UkmSource>> unsent_sources; - int unmatched_sources = 0; + // Number of sources discarded due to not matching a navigation URL. + int num_sources_unmatched = 0; std::unordered_map<ukm::SourceIdType, int> serialized_source_type_counts; - for (auto& kv : recordings_.sources) { + + for (const auto& kv : recordings_.sources) { + // Sources of non-navigation types will not be kept after current report. + if (GetSourceIdType(kv.first) != base::UkmSourceId::Type::NAVIGATION_ID) { + recordings_.obsolete_source_ids.insert(kv.first); + } // If the source id is not whitelisted, don't send it unless it has - // associated entries and the URL matches a URL of a whitelisted source. + // associated entries and the URL matches that of a whitelisted source. // Note: If ShouldRestrictToWhitelistedSourceIds() is true, this logic will // not be hit as the source would have already been filtered in // UpdateSourceURL(). @@ -291,11 +309,12 @@ DCHECK_EQ(1u, kv.second->urls().size()); if (!url_whitelist.count(kv.second->url().spec())) { RecordDroppedSource(DroppedDataReason::NOT_MATCHED); - unmatched_sources++; + MarkSourceForDeletion(kv.first); + num_sources_unmatched++; continue; } - if (!base::Contains(ids_seen, kv.first)) { - unsent_sources.push_back(std::move(kv.second)); + // Omit entryless sources from the report. + if (!base::Contains(source_ids_seen, kv.first)) { continue; } } @@ -353,9 +372,9 @@ UMA_HISTOGRAM_COUNTS_100000("UKM.Entries.SerializedCount2", recordings_.entries.size()); UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.UnsentSourcesCount", - unsent_sources.size()); + num_sources_unsent); UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.UnmatchedSourcesCount", - unmatched_sources); + num_sources_unmatched); UMA_HISTOGRAM_COUNTS_1000( "UKM.Sources.SerializedCount2.Ukm", @@ -367,16 +386,23 @@ "UKM.Sources.SerializedCount2.App", serialized_source_type_counts[ukm::SourceIdType::APP_ID]); + // For each matching id in obsolete_source_ids, remove the Source from + // recordings_.sources. The remaining sources form the deferred sources for + // the next report. + for (const SourceId& source_id : recordings_.obsolete_source_ids) { + recordings_.sources.erase(source_id); + } + recordings_.obsolete_source_ids.clear(); + + // Populate SourceCounts field on the report then clear the recordings. Report::SourceCounts* source_counts_proto = report->mutable_source_counts(); source_counts_proto->set_observed(recordings_.source_counts.observed); source_counts_proto->set_navigation_sources( recordings_.source_counts.navigation_sources); - source_counts_proto->set_unmatched_sources(unmatched_sources); - source_counts_proto->set_deferred_sources(unsent_sources.size()); + source_counts_proto->set_unmatched_sources(num_sources_unmatched); source_counts_proto->set_carryover_sources( recordings_.source_counts.carryover_sources); - recordings_.sources.clear(); recordings_.source_counts.Reset(); recordings_.entries.clear(); recordings_.event_aggregations.clear(); @@ -384,29 +410,36 @@ report->set_is_continuous(recording_is_continuous_); recording_is_continuous_ = true; - // Keep at most |max_kept_sources|, prioritizing most-recent entries (by - // creation time). - const size_t max_kept_sources = GetMaxKeptSources(); - if (unsent_sources.size() > max_kept_sources) { - std::nth_element(unsent_sources.begin(), - unsent_sources.begin() + max_kept_sources, - unsent_sources.end(), - [](const std::unique_ptr<ukm::UkmSource>& lhs, - const std::unique_ptr<ukm::UkmSource>& rhs) { - return lhs->creation_time() > rhs->creation_time(); - }); - unsent_sources.resize(max_kept_sources); + // Defer at most GetMaxKeptSources() sources to the next report, + // prioritizing most recently created ones. + int pruned_sources_age = PruneOldSources(GetMaxKeptSources()); + // Record how old the newest truncated source is. + source_counts_proto->set_pruned_sources_age_seconds(pruned_sources_age); + + // Set deferred sources count after pruning. + source_counts_proto->set_deferred_sources(recordings_.sources.size()); + // Same value as the deferred source count, for setting the carryover count in + // the next reporting cycle. + recordings_.source_counts.carryover_sources = recordings_.sources.size(); + + // We already matched these deferred sources against the URL whitelist. + // Re-whitelist them for the next report. + for (const auto& kv : recordings_.sources) { + recordings_.carryover_urls_whitelist.insert(kv.second->url().spec()); } - for (auto& source : unsent_sources) { - // We already matched these sources against the URL whitelist. - // Re-whitelist them for the next report. - recordings_.carryover_urls_whitelist.insert(source->url().spec()); - recordings_.sources.emplace(source->id(), std::move(source)); - } UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.KeptSourcesCount", recordings_.sources.size()); - recordings_.source_counts.carryover_sources = recordings_.sources.size(); + + // Record number of sources after pruning that were carried over due to not + // having any events in this reporting cycle. + int num_sources_entryless = 0; + for (const auto& kv : recordings_.sources) { + if (!base::Contains(source_ids_seen, kv.first)) { + num_sources_entryless++; + } + } + source_counts_proto->set_entryless_sources(num_sources_entryless); } bool UkmRecorderImpl::ShouldRestrictToWhitelistedSourceIds() const { @@ -418,6 +451,34 @@ return true; } +int UkmRecorderImpl::PruneOldSources(size_t max_kept_sources) { + if (recordings_.sources.size() <= max_kept_sources) + return 0; + + std::vector<std::pair<base::TimeTicks, ukm::SourceId>> + timestamp_source_id_pairs; + for (const auto& kv : recordings_.sources) { + timestamp_source_id_pairs.push_back( + std::pair<base::TimeTicks, ukm::SourceId>(kv.second->creation_time(), + kv.first)); + } + // Partially sort so that the last |max_kept_sources| elements are the + // newest. + std::nth_element(timestamp_source_id_pairs.begin(), + timestamp_source_id_pairs.end() - max_kept_sources, + timestamp_source_id_pairs.end()); + + for (auto kv = timestamp_source_id_pairs.begin(); + kv != timestamp_source_id_pairs.end() - max_kept_sources; ++kv) { + recordings_.sources.erase(kv->second); + } + + base::TimeDelta pruned_sources_age = + base::TimeTicks::Now() - + (timestamp_source_id_pairs.end() - (max_kept_sources + 1))->first; + return pruned_sources_age.InSeconds(); +} + void UkmRecorderImpl::UpdateSourceURL(SourceId source_id, const GURL& unsanitized_url) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -445,7 +506,7 @@ const UkmSource::NavigationData& unsanitized_navigation_data) { DCHECK(GetSourceIdType(source_id) == SourceIdType::NAVIGATION_ID); DCHECK(!base::Contains(recordings_.sources, source_id)); - // TODO(csharrison): Consider changing this behavior so the Source isn't event + // TODO(csharrison): Consider changing this behavior so the Source isn't even // recorded at all if the final URL in |unsanitized_navigation_data| should // not be recorded. std::vector<GURL> urls;
diff --git a/components/ukm/ukm_recorder_impl.h b/components/ukm/ukm_recorder_impl.h index 65039d8..83f9dab 100644 --- a/components/ukm/ukm_recorder_impl.h +++ b/components/ukm/ukm_recorder_impl.h
@@ -19,6 +19,7 @@ #include "base/strings/string_piece.h" #include "services/metrics/public/cpp/ukm_decode.h" #include "services/metrics/public/cpp/ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_source_id.h" #include "services/metrics/public/mojom/ukm_interface.mojom-forward.h" namespace metrics { @@ -67,6 +68,11 @@ // Deletes stored recordings. void Purge(); + // Marks a source as no longer needed to be kept alive in memory. The source + // with given id will be removed from in-memory recordings at the next + // reporting cycle. + void MarkSourceForDeletion(ukm::SourceId source_id) override; + // Sets a callback for determining if an extension URL can be recorded. void SetIsWebstoreExtensionCallback( const IsWebstoreExtensionCallback& callback); @@ -100,6 +106,11 @@ return recordings_.entries; } + // Keep only newest |max_kept_sources| sources when the number of sources + // in recordings_ exceeds this threshold. Returns the age of newest truncated + // source in seconds. + int PruneOldSources(size_t max_kept_sources); + // UkmRecorder: void AddEntry(mojom::UkmEntryPtr entry) override; void UpdateSourceURL(SourceId source_id, const GURL& url) override; @@ -194,6 +205,10 @@ // Data captured by AddEntry(). std::vector<mojom::UkmEntryPtr> entries; + // Source ids that have been marked as no longer needed, to denote the + // subset of |sources| that can be purged after next report. + std::unordered_set<ukm::SourceId> obsolete_source_ids; + // URLs of sources that matched a whitelist url, but were not included in // the report generated by the last log rotation because we haven't seen any // events for that source yet.
diff --git a/components/ukm/ukm_service_unittest.cc b/components/ukm/ukm_service_unittest.cc index 560e33e..5a7dd66 100644 --- a/components/ukm/ukm_service_unittest.cc +++ b/components/ukm/ukm_service_unittest.cc
@@ -23,8 +23,10 @@ #include "components/metrics/test_metrics_provider.h" #include "components/metrics/test_metrics_service_client.h" #include "components/prefs/testing_pref_service.h" -#include "components/ukm/unsent_log_store_metrics_impl.h" #include "components/ukm/ukm_pref_names.h" +#include "components/ukm/ukm_recorder_impl.h" +#include "components/ukm/ukm_service.h" +#include "components/ukm/unsent_log_store_metrics_impl.h" #include "services/metrics/public/cpp/ukm_builders.h" #include "services/metrics/public/cpp/ukm_entry_builder.h" #include "services/metrics/public/cpp/ukm_source.h" @@ -65,6 +67,10 @@ recorder_->RecordNavigation(source_id, navigation_data); } + void MarkSourceForDeletion(SourceId source_id) { + recorder_->MarkSourceForDeletion(source_id); + } + private: UkmRecorder* recorder_; @@ -388,11 +394,13 @@ recorder.UpdateSourceURL(id, GURL("https://google.com/foobar")); TestEvent1(id).Record(&service); + // Do not keep the source in the recorder after the current log. + recorder.MarkSourceForDeletion(id); // Includes a Source and an Entry, so will persist. service.Flush(); EXPECT_EQ(GetPersistedLogCount(), 3); - // Current log has no Sources. + // The recorder contains no Sources or Entries thus will not create a new log. service.Flush(); EXPECT_EQ(GetPersistedLogCount(), 3); } @@ -683,19 +691,28 @@ EXPECT_EQ(1, GetPersistedLogCount()); auto proto_report = GetPersistedReport(); + // The non-whitelisted source should only be recorded if we aren't + // restricted to whitelisted source ids. if (restrict_to_whitelisted_source_ids) { + // Only the one whitelisted source (whitelisted_id) is recorded. EXPECT_EQ(1, proto_report.source_counts().observed()); + // The one whitelisted source is of navigation type. EXPECT_EQ(1, proto_report.source_counts().navigation_sources()); EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(0, proto_report.source_counts().deferred_sources()); + // The one whitelisted source is also deferred for inclusion in future + // reports. + EXPECT_EQ(1, proto_report.source_counts().deferred_sources()); EXPECT_EQ(0, proto_report.source_counts().carryover_sources()); ASSERT_EQ(1, proto_report.sources_size()); } else { + // 1 whitelisted source and 6 non-whitelisted source. EXPECT_EQ(7, proto_report.source_counts().observed()); + // The one whitelisted source is of navigation type. EXPECT_EQ(1, proto_report.source_counts().navigation_sources()); EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(4, proto_report.source_counts().deferred_sources()); + // Only the navigation type source is deferred. + EXPECT_EQ(1, proto_report.source_counts().deferred_sources()); EXPECT_EQ(0, proto_report.source_counts().carryover_sources()); ASSERT_EQ(3, proto_report.sources_size()); @@ -704,7 +721,6 @@ EXPECT_EQ(ids[2], proto_report.sources(1).id()); EXPECT_EQ(kURL.spec(), proto_report.sources(1).url()); } - // Since MaxKeptSources is 3, only Sources 5, 4, 3 should be retained. // Log entries under 0, 1, 3 and 4. Log them in reverse order - which // shouldn't affect source ordering in the output. @@ -720,35 +736,42 @@ EXPECT_EQ(2, GetPersistedLogCount()); proto_report = GetPersistedReport(); + // The non-whitelisted source should only be recorded if we aren't + // restricted to whitelisted source ids. if (restrict_to_whitelisted_source_ids) { EXPECT_EQ(0, proto_report.source_counts().observed()); EXPECT_EQ(0, proto_report.source_counts().navigation_sources()); EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(0, proto_report.source_counts().deferred_sources()); - EXPECT_EQ(0, proto_report.source_counts().carryover_sources()); - - ASSERT_EQ(0, proto_report.sources_size()); + // The one whitelisted source is deferred again in future reports. + EXPECT_EQ(1, proto_report.source_counts().deferred_sources()); + // Number of sources carried over from the previous report to this report. + EXPECT_EQ(1, proto_report.source_counts().carryover_sources()); + // Only the navigation source is again included in current report and + // there is a new entry associated to it. + ASSERT_EQ(1, proto_report.sources_size()); } else { EXPECT_EQ(0, proto_report.source_counts().observed()); EXPECT_EQ(0, proto_report.source_counts().navigation_sources()); EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); + // Only the navigation type source is deferred. EXPECT_EQ(1, proto_report.source_counts().deferred_sources()); - EXPECT_EQ(3, proto_report.source_counts().carryover_sources()); - - ASSERT_EQ(2, proto_report.sources_size()); - EXPECT_EQ(ids[3], proto_report.sources(0).id()); + // Number of sources carried over from the previous report to this report. + EXPECT_EQ(1, proto_report.source_counts().carryover_sources()); + // Only the navigation source is again included in current report and + // there is a new entry associated to it. + ASSERT_EQ(1, proto_report.sources_size()); + EXPECT_EQ(whitelisted_id, proto_report.sources(0).id()); EXPECT_EQ(kURL.spec(), proto_report.sources(0).url()); - EXPECT_EQ(ids[4], proto_report.sources(1).id()); - EXPECT_EQ(kURL.spec(), proto_report.sources(1).url()); } } } TEST_F(UkmServiceTest, NonWhitelistedUrls) { + // URL to be manually whitelisted using whitelisted source type. const GURL kURL("https://google.com/foobar"); struct { GURL url; - bool expected_kept; + bool expect_in_report; } test_cases[] = { {GURL("https://google.com/foobar"), true}, // For origin-only URLs, only the origin needs to be matched. @@ -776,7 +799,7 @@ ukm::SourceId whitelist_id = GetWhitelistedSourceId(1); recorder.UpdateSourceURL(whitelist_id, kURL); - // Record non whitelisted ID with a entry. + // Record non whitelisted ID with an entry. ukm::SourceId nonwhitelist_id = GetNonWhitelistedSourceId(100); recorder.UpdateSourceURL(nonwhitelist_id, test.url); TestEvent1(nonwhitelist_id).Record(&service); @@ -787,7 +810,10 @@ EXPECT_EQ(2, proto_report.source_counts().observed()); EXPECT_EQ(1, proto_report.source_counts().navigation_sources()); - if (test.expected_kept) { + + // If the source id is not whitelisted, don't send it unless it has + // associated entries and the URL matches that of the whitelisted source. + if (test.expect_in_report) { EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); ASSERT_EQ(2, proto_report.sources_size()); EXPECT_EQ(whitelist_id, proto_report.sources(0).id()); @@ -800,121 +826,32 @@ EXPECT_EQ(whitelist_id, proto_report.sources(0).id()); EXPECT_EQ(kURL, proto_report.sources(0).url()); } - } -} -TEST_F(UkmServiceTest, NonWhitelistedCarryoverUrls) { - const GURL kURL("https://google.com/foobar"); - - struct { - // Source1 is recorded during the first rotation with no entry. - // An entry for it is recorded in the second rotation. - GURL source1_url; - // Should Source1 be seen in second rotation's log. - bool expect_source1; - // Source2 is recorded during the second rotation with an entry. - GURL source2_url; - // Should Source2 be seen in second rotation's log. - bool expect_source2; - } test_cases[] = { - // Recording the URL captures in the whitelist, which will also allow - // exact matches of the same URL. - {GURL("https://google.com/foobar"), true, - GURL("https://google.com/foobar"), true}, - // Capturing a full URL shouldn't allow origin matches. - {GURL("https://google.com/foobar"), true, GURL("https://google.com"), - false}, - // Uncaptured URLs won't get matched. - {GURL("https://google.com/foobar"), true, GURL("https://other.com"), - false}, - // Origin should be capturable, and will remember the same origin. - {GURL("https://google.com"), true, GURL("https://google.com"), true}, - // If the origin is captured, only the origin is remembered. - {GURL("https://google.com"), true, GURL("https://google.com/foobar"), - false}, - // Uncaptured URLs won't get matched. - {GURL("https://google.com"), true, GURL("https://other.com"), false}, - // If the URL isn't captured in the first round, it won't capture later. - {GURL("https://other.com"), false, GURL("https://google.com/foobar"), - false}, - {GURL("https://other.com"), false, GURL("https://google.com"), false}, - // Entries shouldn't whitelist themselves. - {GURL("https://other.com"), false, GURL("https://other.com"), false}, - }; - - base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); - ScopedUkmFeatureParams params({{"WhitelistEntries", Entry1And2Whitelist()}}); - - for (const auto& test : test_cases) { - ClearPrefs(); - UkmService service(&prefs_, &client_, - true /* restrict_to_whitelisted_entries */); - TestRecordingHelper recorder(&service); - - EXPECT_EQ(GetPersistedLogCount(), 0); - service.Initialize(); - task_runner_->RunUntilIdle(); - service.EnableRecording(/*extensions=*/false); - service.EnableReporting(); - - // Record with whitelisted ID to whitelist the URL. - ukm::SourceId whitelist_id = GetWhitelistedSourceId(1); - recorder.UpdateSourceURL(whitelist_id, kURL); - - // Record test Source1 without an event. - ukm::SourceId nonwhitelist_id1 = GetNonWhitelistedSourceId(100); - recorder.UpdateSourceURL(nonwhitelist_id1, test.source1_url); - - service.Flush(); - ASSERT_EQ(1, GetPersistedLogCount()); - auto proto_report = GetPersistedReport(); - - EXPECT_EQ(2, proto_report.source_counts().observed()); - EXPECT_EQ(1, proto_report.source_counts().navigation_sources()); - EXPECT_EQ(0, proto_report.source_counts().carryover_sources()); - if (test.expect_source1) { - EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(1, proto_report.source_counts().deferred_sources()); - } else { - EXPECT_EQ(1, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(0, proto_report.source_counts().deferred_sources()); - } - ASSERT_EQ(1, proto_report.sources_size()); - EXPECT_EQ(whitelist_id, proto_report.sources(0).id()); - EXPECT_EQ(kURL, proto_report.sources(0).url()); - - // Record the Source2 and events for Source1 and Source2. + // Do a log rotation again, with the same test URL associated to a new + // source id. Since the previous source id of the test case is of + // non-whitelisted type, the carryover URLs list is expected to remain + // be unchanged, thus the the report should still contain the same numbers + // of sources as before, that is, non-whitelisted URLs should not have + // whitelisted themselves during the previous log rotation. ukm::SourceId nonwhitelist_id2 = GetNonWhitelistedSourceId(101); - recorder.UpdateSourceURL(nonwhitelist_id2, test.source2_url); - TestEvent1(nonwhitelist_id1).Record(&service); + recorder.UpdateSourceURL(nonwhitelist_id2, test.url); TestEvent1(nonwhitelist_id2).Record(&service); - service.Flush(); ASSERT_EQ(2, GetPersistedLogCount()); proto_report = GetPersistedReport(); - EXPECT_EQ(1, proto_report.source_counts().observed()); - EXPECT_EQ(0, proto_report.source_counts().navigation_sources()); - EXPECT_EQ(0, proto_report.source_counts().deferred_sources()); - if (!test.expect_source1) { - EXPECT_FALSE(test.expect_source2); - EXPECT_EQ(1, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(0, proto_report.source_counts().carryover_sources()); - ASSERT_EQ(0, proto_report.sources_size()); - } else if (!test.expect_source2) { - EXPECT_EQ(1, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(1, proto_report.source_counts().carryover_sources()); - ASSERT_EQ(1, proto_report.sources_size()); - EXPECT_EQ(nonwhitelist_id1, proto_report.sources(0).id()); - EXPECT_EQ(test.source1_url, proto_report.sources(0).url()); - } else { + if (test.expect_in_report) { EXPECT_EQ(0, proto_report.source_counts().unmatched_sources()); - EXPECT_EQ(1, proto_report.source_counts().carryover_sources()); ASSERT_EQ(2, proto_report.sources_size()); - EXPECT_EQ(nonwhitelist_id1, proto_report.sources(0).id()); - EXPECT_EQ(test.source1_url, proto_report.sources(0).url()); + EXPECT_EQ(whitelist_id, proto_report.sources(0).id()); + EXPECT_EQ(kURL, proto_report.sources(0).url()); EXPECT_EQ(nonwhitelist_id2, proto_report.sources(1).id()); - EXPECT_EQ(test.source2_url, proto_report.sources(1).url()); + EXPECT_EQ(test.url, proto_report.sources(1).url()); + } else { + EXPECT_EQ(1, proto_report.source_counts().unmatched_sources()); + ASSERT_EQ(1, proto_report.sources_size()); + EXPECT_EQ(whitelist_id, proto_report.sources(0).id()); + EXPECT_EQ(kURL, proto_report.sources(0).url()); } } } @@ -1101,4 +1038,93 @@ } } +TEST_F(UkmServiceTest, MarkSourceForDeletion) { + UkmService service(&prefs_, &client_, + true /* restrict_to_whitelist_entries */); + TestRecordingHelper recorder(&service); + EXPECT_EQ(0, GetPersistedLogCount()); + service.Initialize(); + task_runner_->RunUntilIdle(); + service.EnableRecording(/*extensions=*/false); + service.EnableReporting(); + + // Seed some dummy sources. + SourceId id0 = GetWhitelistedSourceId(0); + recorder.UpdateSourceURL(id0, GURL("https://www.example0.com/")); + SourceId id1 = GetWhitelistedSourceId(1); + recorder.UpdateSourceURL(id1, GURL("https://www.example1.com/")); + SourceId id2 = GetWhitelistedSourceId(2); + recorder.UpdateSourceURL(id2, GURL("https://www.example2.com/")); + + service.Flush(); + int logs_count = 0; + EXPECT_EQ(++logs_count, GetPersistedLogCount()); + + // All sources are present in the report. + Report proto_report = GetPersistedReport(); + ASSERT_EQ(3, proto_report.sources_size()); + EXPECT_EQ(id0, proto_report.sources(0).id()); + EXPECT_EQ(id1, proto_report.sources(1).id()); + EXPECT_EQ(id2, proto_report.sources(2).id()); + + // Mark source 1 for deletion. Next report will still contain source 1 because + // we might have associated entries. It will no longer be in further report at + // the following cycle. + service.MarkSourceForDeletion(id1); + service.Flush(); + EXPECT_EQ(++logs_count, GetPersistedLogCount()); + + proto_report = GetPersistedReport(); + ASSERT_EQ(3, proto_report.sources_size()); + + service.Flush(); + EXPECT_EQ(++logs_count, GetPersistedLogCount()); + + proto_report = GetPersistedReport(); + ASSERT_EQ(2, proto_report.sources_size()); + EXPECT_EQ(id0, proto_report.sources(0).id()); + EXPECT_EQ(id2, proto_report.sources(1).id()); +} + +TEST_F(UkmServiceTest, PurgeNonNavigationSources) { + UkmService service(&prefs_, &client_, + true /* restrict_to_whitelist_entries */); + TestRecordingHelper recorder(&service); + EXPECT_EQ(0, GetPersistedLogCount()); + service.Initialize(); + task_runner_->RunUntilIdle(); + service.EnableRecording(/*extensions=*/false); + service.EnableReporting(); + + // Seed some dummy sources. + SourceId id0 = ConvertToSourceId(0, SourceIdType::UKM); + recorder.UpdateSourceURL(id0, GURL("https://www.example0.com/")); + SourceId id1 = ConvertToSourceId(1, SourceIdType::NAVIGATION_ID); + recorder.UpdateSourceURL(id1, GURL("https://www.example1.com/")); + SourceId id2 = ConvertToSourceId(2, SourceIdType::APP_ID); + recorder.UpdateSourceURL(id2, GURL("https://www.example2.com/")); + SourceId id3 = ConvertToSourceId(3, SourceIdType::HISTORY_ID); + recorder.UpdateSourceURL(id3, GURL("https://www.example3.com/")); + + service.Flush(); + int logs_count = 0; + EXPECT_EQ(++logs_count, GetPersistedLogCount()); + + // All sources are present except id0 of non-whitelisted UKM type. + Report proto_report = GetPersistedReport(); + ASSERT_EQ(3, proto_report.sources_size()); + EXPECT_EQ(id1, proto_report.sources(0).id()); + EXPECT_EQ(id2, proto_report.sources(1).id()); + EXPECT_EQ(id3, proto_report.sources(2).id()); + + service.Flush(); + EXPECT_EQ(++logs_count, GetPersistedLogCount()); + + // Sources of APP_ID and HISTORY_ID types are not kept between reporting + // cycles, thus only 1 navigation type source remains. + proto_report = GetPersistedReport(); + ASSERT_EQ(1, proto_report.sources_size()); + EXPECT_EQ(id1, proto_report.sources(0).id()); +} + } // namespace ukm
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 541424af..abab7162 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -22,7 +22,7 @@ // (OOP-D). // TODO(dnicoara): Look at enabling Chromecast support when ChromeOS support is // ready. -#if defined(OS_CHROMEOS) || defined(IS_CHROMECAST) +#if defined(IS_CHROMECAST) const base::Feature kVizDisplayCompositor{"VizDisplayCompositor", base::FEATURE_DISABLED_BY_DEFAULT}; #else
diff --git a/components/viz/service/display/ca_layer_overlay.cc b/components/viz/service/display/ca_layer_overlay.cc index 5330772..481f8a2 100644 --- a/components/viz/service/display/ca_layer_overlay.cc +++ b/components/viz/service/display/ca_layer_overlay.cc
@@ -202,10 +202,6 @@ // but requires APIs not supported on all macOS versions. if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty()) { DCHECK(quad->shared_quad_state->is_clipped); - if (quad->shared_quad_state->rounded_corner_bounds.rect() != - gfx::RectF(quad->shared_quad_state->clip_rect)) { - return CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_CLIP_MISMATCH; - } if (quad->shared_quad_state->rounded_corner_bounds.GetType() > gfx::RRectF::Type::kSingle) { return CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_NOT_UNIFORM; @@ -233,8 +229,8 @@ quad->shared_quad_state->is_clipped; most_recent_overlay_shared_state_->clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect); - most_recent_overlay_shared_state_->clip_rect_corner_radius = - quad->shared_quad_state->rounded_corner_bounds.GetSimpleRadius(); + most_recent_overlay_shared_state_->rounded_corner_bounds = + quad->shared_quad_state->rounded_corner_bounds; most_recent_overlay_shared_state_->opacity = quad->shared_quad_state->opacity;
diff --git a/components/viz/service/display/ca_layer_overlay.h b/components/viz/service/display/ca_layer_overlay.h index 15cf19f..d8004ff3 100644 --- a/components/viz/service/display/ca_layer_overlay.h +++ b/components/viz/service/display/ca_layer_overlay.h
@@ -12,6 +12,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/rrect_f.h" #include "ui/gl/ca_renderer_layer_params.h" namespace viz { @@ -32,7 +33,7 @@ // |clip_rect_corner_radius| represents the corner radius of the clip rect. bool is_clipped = false; gfx::RectF clip_rect; - float clip_rect_corner_radius = 0; + gfx::RRectF rounded_corner_bounds; // The opacity property for the CAayer. float opacity = 1; // The transform to apply to the CALayer.
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc index 8dfe123..bd87811 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc
@@ -3342,8 +3342,13 @@ ca_layer_overlay.shared_state->clip_rect.y(), ca_layer_overlay.shared_state->clip_rect.width(), ca_layer_overlay.shared_state->clip_rect.height()}; - GLfloat clip_rect_corner_radius = - ca_layer_overlay.shared_state->clip_rect_corner_radius; + + const gfx::RectF& rect = + ca_layer_overlay.shared_state->rounded_corner_bounds.rect(); + GLfloat rounded_corner_bounds[5] = { + rect.x(), rect.y(), rect.width(), rect.height(), + ca_layer_overlay.shared_state->rounded_corner_bounds.GetSimpleRadius()}; + GLint sorting_context_id = ca_layer_overlay.shared_state->sorting_context_id; GLfloat transform[16]; @@ -3354,7 +3359,7 @@ shared_state = ca_layer_overlay.shared_state; gl_->ScheduleCALayerSharedStateCHROMIUM( ca_layer_overlay.shared_state->opacity, is_clipped, clip_rect, - clip_rect_corner_radius, sorting_context_id, transform); + rounded_corner_bounds, sorting_context_id, transform); } gl_->ScheduleCALayerCHROMIUM( texture_id, contents_rect, ca_layer_overlay.background_color, @@ -3676,8 +3681,13 @@ ca_layer_overlay->shared_state->clip_rect.y(), ca_layer_overlay->shared_state->clip_rect.width(), ca_layer_overlay->shared_state->clip_rect.height()}; - GLfloat clip_rect_corner_radius = - ca_layer_overlay->shared_state->clip_rect_corner_radius; + + const gfx::RectF& rect = + ca_layer_overlay->shared_state->rounded_corner_bounds.rect(); + GLfloat rounded_corner_rect[5] = { + rect.x(), rect.y(), rect.width(), rect.height(), + ca_layer_overlay->shared_state->rounded_corner_bounds.GetSimpleRadius()}; + GLint sorting_context_id = ca_layer_overlay->shared_state->sorting_context_id; SkMatrix44 transform = ca_layer_overlay->shared_state->transform; GLfloat gl_transform[16]; @@ -3687,7 +3697,7 @@ // The alpha has already been applied when copying the RPDQ to an IOSurface. GLfloat alpha = 1; gl_->ScheduleCALayerSharedStateCHROMIUM(alpha, is_clipped, clip_rect, - clip_rect_corner_radius, + rounded_corner_rect, sorting_context_id, gl_transform); gl_->ScheduleCALayerCHROMIUM(overlay_texture->texture.id(), contents_rect, ca_layer_overlay->background_color,
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc index d7f2d20..5e38126d 100644 --- a/components/viz/service/display/gl_renderer_unittest.cc +++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -3136,7 +3136,7 @@ void(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform)); MOCK_METHOD6(ScheduleCALayerCHROMIUM, @@ -3313,16 +3313,15 @@ case 0: // Subtest 0 is a simple round rect that matches the clip rect, and // should be handled by CALayers. - EXPECT_CALL(gl(), - ScheduleCALayerSharedStateCHROMIUM(_, _, _, radius, _, _)) + EXPECT_CALL(gl(), ScheduleCALayerSharedStateCHROMIUM(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1); break; case 1: - // Subtest 1 doesn't match clip and rounded rect, so no CALayers should - // be scheduled. + // Subtest 1 doesn't match clip and rounded rect, but we can still + // use CALayers. sqs->clip_rect = gfx::Rect(3, 3, 4, 4); - EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(gl(), ScheduleCALayerCHROMIUM(_, _, _, _, _, _)).Times(1); break; case 2: // Subtest 2 has a non-simple rounded rect.
diff --git a/components/viz/service/display_embedder/software_output_device_x11.cc b/components/viz/service/display_embedder/software_output_device_x11.cc index e91ff43..ab13e1e3 100644 --- a/components/viz/service/display_embedder/software_output_device_x11.cc +++ b/components/viz/service/display_embedder/software_output_device_x11.cc
@@ -39,6 +39,10 @@ DISALLOW_COPY_AND_ASSIGN(ScopedPixmap); }; +struct XImageDeleter { + void operator()(XImage* image) const { XDestroyImage(image); } +}; + // Draw |data| over |widget|'s parent-relative background, and write the // resulting image to |widget|. Returns true on success. bool CompositeBitmap(XDisplay* display, @@ -52,7 +56,7 @@ const void* data) { XClearArea(display, widget, x, y, width, height, false); - std::unique_ptr<XImage, ui::XImageDeleter> bg; + std::unique_ptr<XImage, XImageDeleter> bg; { gfx::X11ErrorTracker ignore_x_errors; bg.reset(
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index 79591a0e..b295397 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -355,29 +355,11 @@ base::Optional<HitTestRegionList> hit_test_region_list, uint64_t submit_time, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback callback) { - TRACE_EVENT1("viz", "CompositorFrameSinkSupport::MaybeSubmitCompositorFrame", - "FrameSinkId", frame_sink_id_.ToString()); - - TRACE_EVENT_WITH_FLOW1( + TRACE_EVENT_WITH_FLOW2( "viz,benchmark", "Graphics.Pipeline", TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", - "ReceiveCompositorFrame"); - - TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "SubmitCompositorFrame", local_surface_id.hash()); - - bool tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - &tracing_enabled); - if (tracing_enabled) { - base::TimeDelta elapsed = base::TimeTicks::Now().since_origin() - - base::TimeDelta::FromMicroseconds(submit_time); - TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "SubmitCompositorFrame::TimeElapsed", - TRACE_EVENT_SCOPE_THREAD, - "elapsed time:", elapsed.InMicroseconds()); - } + "ReceiveCompositorFrame", "FrameSinkId", frame_sink_id_.ToString()); DCHECK(local_surface_id.is_valid()); DCHECK(!frame.render_pass_list.empty());
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc index 8077ca72..6839d8ac 100644 --- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -486,7 +486,8 @@ } // http://crbug.com/982998 -#if defined(OS_WIN) +// Test fails on Mac due to https://crbug.com/985925. +#if defined(OS_WIN) || defined(OS_MACOSX) #define MAYBE_AccessibilityEventsExpandedChange \ DISABLED_AccessibilityEventsExpandedChange #else
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc index d4779954..6bf2055 100644 --- a/content/browser/background_sync/background_sync_manager.cc +++ b/content/browser/background_sync/background_sync_manager.cc
@@ -79,9 +79,10 @@ const char kBackgroundSyncUserDataKey[] = "BackgroundSyncUserData"; void RecordFailureAndPostError( + BackgroundSyncType sync_type, BackgroundSyncStatus status, BackgroundSyncManager::StatusAndRegistrationCallback callback) { - BackgroundSyncMetrics::CountRegisterFailure(status); + BackgroundSyncMetrics::CountRegisterFailure(sync_type, status); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), status, nullptr)); @@ -347,7 +348,8 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); if (disabled_) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_STORAGE_ERROR, std::move(callback)); return; } @@ -659,7 +661,8 @@ ServiceWorkerRegistration* sw_registration = service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, std::move(callback)); return; } @@ -679,7 +682,8 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!has_main_frame_client) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_NOT_ALLOWED, std::move(callback)); return; } @@ -693,13 +697,15 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); if (disabled_) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_STORAGE_ERROR, std::move(callback)); return; } if (options.tag.length() > kMaxTagLength) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_NOT_ALLOWED, std::move(callback)); return; } @@ -707,7 +713,8 @@ ServiceWorkerRegistration* sw_registration = service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, std::move(callback)); return; } @@ -731,7 +738,8 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); if (permission_statuses.first == PermissionStatus::DENIED) { - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_PERMISSION_DENIED, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_PERMISSION_DENIED, std::move(callback)); return; } @@ -741,7 +749,8 @@ service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { // The service worker was shut down in the interim. - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + RecordFailureAndPostError(GetBackgroundSyncType(options), + BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, std::move(callback)); return; } @@ -783,6 +792,7 @@ ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; BackgroundSyncMetrics::CountRegisterSuccess( + existing_registration->sync_type(), options.min_interval, registration_could_fire, BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); @@ -838,14 +848,16 @@ // We don't fire periodic Background Sync registrations immediately after // registration, so set delay_until to override its default value. if (registration.sync_type() == BackgroundSyncType::PERIODIC) { - registration.set_delay_until(clock_->Now() + delay); + registration.set_delay_until(GetDelayUntilAfterApplyingMinGapForOrigin( + registration.origin(), delay)); } ServiceWorkerRegistration* sw_registration = service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { // The service worker was shut down in the interim. - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + RecordFailureAndPostError(registration.sync_type(), + BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, std::move(callback)); return; } @@ -1024,14 +1036,15 @@ if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) { // The service worker registration is gone. active_registrations_.erase(sw_registration_id); - RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + RecordFailureAndPostError(registration.sync_type(), + BACKGROUND_SYNC_STATUS_STORAGE_ERROR, std::move(callback)); return; } if (status != blink::ServiceWorkerStatusCode::kOk) { BackgroundSyncMetrics::CountRegisterFailure( - BACKGROUND_SYNC_STATUS_STORAGE_ERROR); + registration.sync_type(), BACKGROUND_SYNC_STATUS_STORAGE_ERROR); DisableAndClearManager(base::BindOnce( std::move(callback), BACKGROUND_SYNC_STATUS_STORAGE_ERROR, nullptr)); return; @@ -1042,6 +1055,7 @@ ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; BackgroundSyncMetrics::CountRegisterSuccess( + registration.sync_type(), registration.options()->min_interval, registration_could_fire, BackgroundSyncMetrics::REGISTRATION_IS_NOT_DUPLICATE); @@ -1388,6 +1402,50 @@ return std::max(soonest_wakeup_delta, time_till_next_allowed_browser_wakeup); } +base::Time BackgroundSyncManager::GetDelayUntilAfterApplyingMinGapForOrigin( + const url::Origin& origin, + base::TimeDelta delay) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + base::Time now_plus_delay = clock_->Now() + delay; + base::Time soonest_wakeup_time_for_origin = + GetSoonestPeriodicSyncEventTimeForOrigin(origin); + if (soonest_wakeup_time_for_origin.is_null()) + return now_plus_delay; + + if (now_plus_delay <= soonest_wakeup_time_for_origin) + return soonest_wakeup_time_for_origin; + else + return soonest_wakeup_time_for_origin + delay; +} + +base::Time BackgroundSyncManager::GetSoonestPeriodicSyncEventTimeForOrigin( + const url::Origin& origin) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + base::Time soonest_wakeup_time = base::Time(); + for (const auto& active_registration : active_registrations_) { + if (active_registration.second.origin != origin) + continue; + + const auto& tag_and_registrations = + active_registration.second.registration_map; + for (const auto& tag_and_registration : tag_and_registrations) { + if (/* sync_type= */ tag_and_registration.first.second != + BackgroundSyncType::PERIODIC) { + continue; + } + if (tag_and_registration.second.delay_until().is_null()) + continue; + if (soonest_wakeup_time.is_null() || + tag_and_registration.second.delay_until() < soonest_wakeup_time) { + soonest_wakeup_time = tag_and_registration.second.delay_until(); + } + } + } + return soonest_wakeup_time; +} + void BackgroundSyncManager::ScheduleDelayedProcessingOfRegistrations( blink::mojom::BackgroundSyncType sync_type) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -1474,8 +1532,8 @@ // Record the total time taken after all events have run to completion. base::RepeatingClosure events_completed_barrier_closure = base::BarrierClosure(to_fire.size(), - base::BindOnce(&OnAllSyncEventsCompleted, start_time, - to_fire.size())); + base::BindOnce(&OnAllSyncEventsCompleted, sync_type, + start_time, to_fire.size())); for (auto& registration_info : to_fire) { const BackgroundSyncRegistration* registration = @@ -1537,11 +1595,11 @@ const bool last_chance = registration->num_attempts() == registration->max_attempts() - 1; + auto sync_type = registration_info->sync_type; HasMainFrameProviderHost( url::Origin::Create(service_worker_registration->scope().GetOrigin()), - base::BindOnce(&BackgroundSyncMetrics::RecordEventStarted)); + base::BindOnce(&BackgroundSyncMetrics::RecordEventStarted, sync_type)); - auto sync_type = registration_info->sync_type; if (sync_type == BackgroundSyncType::ONE_SHOT) { DispatchSyncEvent( registration->options()->tag, @@ -1597,6 +1655,7 @@ HasMainFrameProviderHost( origin, base::BindOnce(&BackgroundSyncMetrics::RecordEventResult, + registration_info->sync_type, status_code == blink::ServiceWorkerStatusCode::kOk)); op_scheduler_.ScheduleOperation( @@ -1701,7 +1760,8 @@ registration->sync_type() == BackgroundSyncType::PERIODIC) { registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING); registration_completed = false; - registration->set_delay_until(clock_->Now() + delay); + registration->set_delay_until(GetDelayUntilAfterApplyingMinGapForOrigin( + registration->origin(), delay)); if (ShouldLogToDevTools(registration->sync_type())) { std::string delay_ms = delay.is_max() @@ -1786,11 +1846,13 @@ // static void BackgroundSyncManager::OnAllSyncEventsCompleted( + BackgroundSyncType sync_type, const base::TimeTicks& start_time, int number_of_batched_sync_events) { // Record the combined time taken by all sync events. BackgroundSyncMetrics::RecordBatchSyncEventComplete( - base::TimeTicks::Now() - start_time, number_of_batched_sync_events); + sync_type, base::TimeTicks::Now() - start_time, + number_of_batched_sync_events); } void BackgroundSyncManager::OnRegistrationDeletedImpl(
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h index f7ef273..cd0ae24 100644 --- a/content/browser/background_sync/background_sync_manager.h +++ b/content/browser/background_sync/background_sync_manager.h
@@ -166,6 +166,17 @@ base::TimeDelta wakeup_delta, base::Time last_browser_wakeup_time); + // Each origin has a max_frequency decided by the browser. This picks the + // correct starting point to add to |delay| to so that the resulting + // |delay_until| for the |registration| ensures the minimum gap between + // periodicsync events fired for the origin. + base::Time GetDelayUntilAfterApplyingMinGapForOrigin( + const url::Origin& origin, + base::TimeDelta delay) const; + + base::Time GetSoonestPeriodicSyncEventTimeForOrigin( + const url::Origin& origin) const; + protected: BackgroundSyncManager( scoped_refptr<ServiceWorkerContextWrapper> context, @@ -366,8 +377,10 @@ blink::ServiceWorkerStatusCode status_code); // Called when all sync events have completed. - static void OnAllSyncEventsCompleted(const base::TimeTicks& start_time, - int number_of_batched_sync_events); + static void OnAllSyncEventsCompleted( + blink::mojom::BackgroundSyncType sync_type, + const base::TimeTicks& start_time, + int number_of_batched_sync_events); // OnRegistrationDeleted callbacks void OnRegistrationDeletedImpl(int64_t sw_registration_id,
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc index ff1f498..abb2a6b 100644 --- a/content/browser/background_sync/background_sync_manager_unittest.cc +++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -1723,6 +1723,44 @@ Unregister(sync_options_1_); } +TEST_F(BackgroundSyncManagerTest, StaggeredPeriodicSyncRegistrations) { + base::TimeDelta twelve_hours = base::TimeDelta::FromHours(12); + SetPeriodicSyncEventsMinIntervalAndRestartManager(twelve_hours); + InitPeriodicSyncEventTest(); + SetNetwork(network::mojom::ConnectionType::CONNECTION_NONE); + + // Register a periodic sync. + base::TimeDelta thirteen_hours = base::TimeDelta::FromHours(13); + sync_options_1_.min_interval = thirteen_hours.InMilliseconds(); + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_TRUE(GetRegistration(sync_options_1_)); + + EXPECT_EQ(GetSoonestWakeupDelta( + blink::mojom::BackgroundSyncType::PERIODIC, + /* last_browser_wakeup_for_periodic_sync= */ base::Time()), + thirteen_hours); + + // Advance the clock by an hour. Add another registration. + base::TimeDelta one_hour = base::TimeDelta::FromHours(1); + test_clock_.Advance(one_hour); + sync_options_2_.min_interval = thirteen_hours.InMilliseconds(); + EXPECT_EQ(GetSoonestWakeupDelta( + blink::mojom::BackgroundSyncType::PERIODIC, + /* last_browser_wakeup_for_periodic_sync= */ base::Time()), + twelve_hours); + + // Advance the clock by 12 hours, and enable network connectivity, so the + // first registration fires. Expect the next wakeup time to be longer than 1 + // hour, which is the stagger interval between the two registrations. + test_clock_.Advance(twelve_hours); + SetNetwork(network::mojom::ConnectionType::CONNECTION_WIFI); + base::RunLoop().RunUntilIdle(); + EXPECT_GT(GetSoonestWakeupDelta( + blink::mojom::BackgroundSyncType::PERIODIC, + /* last_browser_wakeup_for_periodic_sync= */ base::Time()), + one_hour); +} + TEST_F(BackgroundSyncManagerTest, OneAttempt) { SetMaxSyncAttemptsAndRestartManager(1); InitFailedSyncEventTest();
diff --git a/content/browser/background_sync/background_sync_metrics.cc b/content/browser/background_sync/background_sync_metrics.cc index 8bff40f..d46ea38c 100644 --- a/content/browser/background_sync/background_sync_metrics.cc +++ b/content/browser/background_sync/background_sync_metrics.cc
@@ -4,28 +4,38 @@ #include "content/browser/background_sync/background_sync_metrics.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/user_metrics_action.h" +#include "base/metrics/histogram_functions.h" namespace { -// ResultPattern is used by Histograms, append new entries at the end. -enum ResultPattern { - RESULT_PATTERN_SUCCESS_FOREGROUND = 0, - RESULT_PATTERN_SUCCESS_BACKGROUND, - RESULT_PATTERN_FAILED_FOREGROUND, - RESULT_PATTERN_FAILED_BACKGROUND, - RESULT_PATTERN_MAX = RESULT_PATTERN_FAILED_BACKGROUND -}; - -ResultPattern EventResultToResultPattern(bool success, - bool finished_in_foreground) { +content::BackgroundSyncMetrics::ResultPattern EventResultToResultPattern( + bool success, + bool finished_in_foreground) { if (success) { - return finished_in_foreground ? RESULT_PATTERN_SUCCESS_FOREGROUND - : RESULT_PATTERN_SUCCESS_BACKGROUND; + return finished_in_foreground ? content::BackgroundSyncMetrics:: + RESULT_PATTERN_SUCCESS_FOREGROUND + : content::BackgroundSyncMetrics:: + RESULT_PATTERN_SUCCESS_BACKGROUND; } - return finished_in_foreground ? RESULT_PATTERN_FAILED_FOREGROUND - : RESULT_PATTERN_FAILED_BACKGROUND; + return finished_in_foreground + ? content::BackgroundSyncMetrics::RESULT_PATTERN_FAILED_FOREGROUND + : content::BackgroundSyncMetrics::RESULT_PATTERN_FAILED_BACKGROUND; +} + +const std::string GetBackgroundSyncSuffix( + blink::mojom::BackgroundSyncType sync_type) { + if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) + return "OneShot"; + else + return "Periodic"; +} + +const std::string GetBackgroundSyncPrefix( + blink::mojom::BackgroundSyncType sync_type) { + if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) + return ""; + else + return "Periodic"; } } // namespace @@ -33,73 +43,105 @@ namespace content { // static -void BackgroundSyncMetrics::RecordEventStarted(bool started_in_foreground) { - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Event.OneShotStartedInForeground", - started_in_foreground); +void BackgroundSyncMetrics::RecordEventStarted( + blink::mojom::BackgroundSyncType sync_type, + bool started_in_foreground) { + base::UmaHistogramBoolean("BackgroundSync.Event." + + GetBackgroundSyncSuffix(sync_type) + + "StartedInForeground", + started_in_foreground); } // static void BackgroundSyncMetrics::RecordRegistrationComplete( bool event_succeeded, int num_attempts_required) { - UMA_HISTOGRAM_BOOLEAN( + base::UmaHistogramBoolean( "BackgroundSync.Registration.OneShot.EventSucceededAtCompletion", event_succeeded); if (!event_succeeded) return; - UMA_HISTOGRAM_EXACT_LINEAR( + base::UmaHistogramExactLinear( "BackgroundSync.Registration.OneShot.NumAttemptsForSuccessfulEvent", num_attempts_required, 50); } // static -void BackgroundSyncMetrics::RecordEventResult(bool success, - bool finished_in_foreground) { - UMA_HISTOGRAM_ENUMERATION( - "BackgroundSync.Event.OneShotResultPattern", +void BackgroundSyncMetrics::RecordEventResult( + blink::mojom::BackgroundSyncType sync_type, + bool success, + bool finished_in_foreground) { + base::UmaHistogramEnumeration( + "BackgroundSync.Event." + GetBackgroundSyncSuffix(sync_type) + + "ResultPattern", EventResultToResultPattern(success, finished_in_foreground), - RESULT_PATTERN_MAX + 1); + static_cast<ResultPattern>(RESULT_PATTERN_MAX + 1)); } // static void BackgroundSyncMetrics::RecordBatchSyncEventComplete( + blink::mojom::BackgroundSyncType sync_type, const base::TimeDelta& time, int number_of_batched_sync_events) { // The total batch handling time should be under 5 minutes; we'll record up to // 6 minutes, to be safe. - UMA_HISTOGRAM_CUSTOM_TIMES("BackgroundSync.Event.Time", time, - base::TimeDelta::FromMilliseconds(10), - base::TimeDelta::FromMinutes(6), 50); - UMA_HISTOGRAM_COUNTS_100("BackgroundSync.Event.BatchSize", - number_of_batched_sync_events); + base::UmaHistogramCustomTimes( + GetBackgroundSyncPrefix(sync_type) + "BackgroundSync.Event.Time", time, + /* min= */ base::TimeDelta::FromMilliseconds(10), + /* max= */ base::TimeDelta::FromMinutes(6), + /* buckets= */ 50); + base::UmaHistogramCounts100( + GetBackgroundSyncPrefix(sync_type) + "BackgroundSync.Event.BatchSize", + number_of_batched_sync_events); } // static void BackgroundSyncMetrics::CountRegisterSuccess( + blink::mojom::BackgroundSyncType sync_type, + int64_t min_interval_ms, RegistrationCouldFire registration_could_fire, RegistrationIsDuplicate registration_is_duplicate) { - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", - BACKGROUND_SYNC_STATUS_OK, - BACKGROUND_SYNC_STATUS_MAX + 1); - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Registration.OneShot.CouldFire", - registration_could_fire == REGISTRATION_COULD_FIRE); - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Registration.OneShot.IsDuplicate", - registration_is_duplicate == REGISTRATION_IS_DUPLICATE); + base::UmaHistogramEnumeration( + "BackgroundSync.Registration." + GetBackgroundSyncSuffix(sync_type), + BACKGROUND_SYNC_STATUS_OK, + static_cast<BackgroundSyncStatus>(BACKGROUND_SYNC_STATUS_MAX + 1)); + + if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT) { + base::UmaHistogramBoolean( + "BackgroundSync.Registration.OneShot.CouldFire", + registration_could_fire == REGISTRATION_COULD_FIRE); + } else { + DCHECK_GE(min_interval_ms, 0); + base::UmaHistogramCounts10M( + "BackgroundSync.Registration.Periodic.MinInterval", + min_interval_ms / 1000); + } + + base::UmaHistogramBoolean( + "BackgroundSync.Registration." + GetBackgroundSyncSuffix(sync_type) + + ".IsDuplicate", + registration_is_duplicate == REGISTRATION_IS_DUPLICATE); } // static -void BackgroundSyncMetrics::CountRegisterFailure(BackgroundSyncStatus result) { - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", result, - BACKGROUND_SYNC_STATUS_MAX + 1); +void BackgroundSyncMetrics::CountRegisterFailure( + blink::mojom::BackgroundSyncType sync_type, + BackgroundSyncStatus result) { + base::UmaHistogramEnumeration( + std::string("BackgroundSync.Registration.") + + GetBackgroundSyncSuffix(sync_type), + result, + static_cast<BackgroundSyncStatus>(BACKGROUND_SYNC_STATUS_MAX + 1)); } // static void BackgroundSyncMetrics::CountUnregisterPeriodicSync( BackgroundSyncStatus status) { - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Unregistration.Periodic", status, - BACKGROUND_SYNC_STATUS_MAX + 1); + base::UmaHistogramEnumeration( + "BackgroundSync.Unregistration.Periodic", status, + static_cast<BackgroundSyncStatus>(BACKGROUND_SYNC_STATUS_MAX + 1)); } } // namespace content
diff --git a/content/browser/background_sync/background_sync_metrics.h b/content/browser/background_sync/background_sync_metrics.h index 36bb7ded..2cb66bae6 100644 --- a/content/browser/background_sync/background_sync_metrics.h +++ b/content/browser/background_sync/background_sync_metrics.h
@@ -9,6 +9,8 @@ #include "base/time/time.h" #include "content/browser/background_sync/background_sync.pb.h" #include "content/browser/background_sync/background_sync_status.h" +#include "content/common/content_export.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" namespace content { @@ -16,8 +18,17 @@ // Sync API. It is stateless, containing only static methods, so it can be used // by any of the Background Sync code, without needing to be instantiated // explicitly. -class BackgroundSyncMetrics { +class CONTENT_EXPORT BackgroundSyncMetrics { public: + // ResultPattern is used by Histograms, append new entries at the end. + enum ResultPattern { + RESULT_PATTERN_SUCCESS_FOREGROUND = 0, + RESULT_PATTERN_SUCCESS_BACKGROUND = 1, + RESULT_PATTERN_FAILED_FOREGROUND = 2, + RESULT_PATTERN_FAILED_BACKGROUND = 3, + RESULT_PATTERN_MAX = RESULT_PATTERN_FAILED_BACKGROUND, + }; + enum RegistrationCouldFire { REGISTRATION_COULD_NOT_FIRE, REGISTRATION_COULD_FIRE @@ -29,10 +40,13 @@ }; // Records the start of a sync event. - static void RecordEventStarted(bool startedin_foreground); + static void RecordEventStarted(blink::mojom::BackgroundSyncType sync_type, + bool startedin_foreground); // Records the result of a single sync event firing. - static void RecordEventResult(bool result, bool finished_in_foreground); + static void RecordEventResult(blink::mojom::BackgroundSyncType sync_type, + bool result, + bool finished_in_foreground); // Records, at the completion of a one-shot sync registration, whether the // sync event was successful, and how many attempts it took to get there. @@ -41,18 +55,27 @@ // Records the result of running a batch of sync events, including the total // time spent, and the batch size. - static void RecordBatchSyncEventComplete(const base::TimeDelta& time, - int number_of_batched_sync_events); + static void RecordBatchSyncEventComplete( + blink::mojom::BackgroundSyncType sync_type, + const base::TimeDelta& time, + int number_of_batched_sync_events); // Records the result of successfully registering a sync. |could_fire| // indicates whether the conditions were sufficient for the sync to fire - // immediately at the time it was registered. + // immediately at the time it was registered. |could_fire| is only relevant to + // and recorded for one-shot Background Sync registrations. + // |min_interval_ms| is only recorded for Periodic Background Sync + // registrations, and records the min_interval in ms requested for this + // registration. static void CountRegisterSuccess( + blink::mojom::BackgroundSyncType sync_type, + int64_t min_interval_ms, RegistrationCouldFire could_fire, RegistrationIsDuplicate registration_is_duplicate); // Records the status of a failed sync registration. - static void CountRegisterFailure(BackgroundSyncStatus status); + static void CountRegisterFailure(blink::mojom::BackgroundSyncType sync_type, + BackgroundSyncStatus status); // Records the status of an attempt to remove a Periodic Background // Sync registration.
diff --git a/content/browser/background_sync/background_sync_metrics_unittest.cc b/content/browser/background_sync/background_sync_metrics_unittest.cc new file mode 100644 index 0000000..ce15e95 --- /dev/null +++ b/content/browser/background_sync/background_sync_metrics_unittest.cc
@@ -0,0 +1,115 @@ +// 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 "base/test/metrics/histogram_tester.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "content/browser/background_sync/background_sync_metrics.h" +#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h" + +namespace content { + +using blink::mojom::BackgroundSyncType; + +class BackgroundSyncMetricsTest : public ::testing::Test { + public: + BackgroundSyncMetricsTest() = default; + ~BackgroundSyncMetricsTest() override = default; + + protected: + base::HistogramTester histogram_tester_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundSyncMetricsTest); +}; + +TEST_F(BackgroundSyncMetricsTest, RecordEventStarted) { + BackgroundSyncMetrics::RecordEventStarted(BackgroundSyncType::ONE_SHOT, + /* started_in_foreground= */ false); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Event.OneShotStartedInForeground", false, 1); + BackgroundSyncMetrics::RecordEventStarted(BackgroundSyncType::PERIODIC, + /* started_in_foreground= */ true); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Event.PeriodicStartedInForeground", true, 1); +} + +TEST_F(BackgroundSyncMetricsTest, RecordRegistrationComplete) { + BackgroundSyncMetrics::RecordRegistrationComplete( + /* event_succeeded= */ true, /* num_attempts_required= */ 3); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Registration.OneShot.EventSucceededAtCompletion", true, + 1); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Registration.OneShot.NumAttemptsForSuccessfulEvent", 3, + 1); +} + +TEST_F(BackgroundSyncMetricsTest, RecordEventResult) { + BackgroundSyncMetrics::RecordEventResult(BackgroundSyncType::ONE_SHOT, + /* event_succeeded= */ true, + /* finished_in_foreground= */ true); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Event.OneShotResultPattern", + BackgroundSyncMetrics::ResultPattern::RESULT_PATTERN_SUCCESS_FOREGROUND, + 1); + + BackgroundSyncMetrics::RecordEventResult(BackgroundSyncType::PERIODIC, + /* event_succeeded= */ false, + /* finished_in_foreground= */ false); + histogram_tester_.ExpectBucketCount( + "BackgroundSync.Event.PeriodicResultPattern", + BackgroundSyncMetrics::ResultPattern::RESULT_PATTERN_FAILED_BACKGROUND, + 1); +} + +TEST_F(BackgroundSyncMetricsTest, RecordBatchSyncEventComplete) { + BackgroundSyncMetrics::RecordBatchSyncEventComplete( + BackgroundSyncType::ONE_SHOT, base::TimeDelta::FromSeconds(1), + /* number_of_batched_sync_events= */ 1); + histogram_tester_.ExpectUniqueSample( + "BackgroundSync.Event.Time", + base::TimeDelta::FromSeconds(1).InMilliseconds(), 1); + + BackgroundSyncMetrics::RecordBatchSyncEventComplete( + BackgroundSyncType::PERIODIC, base::TimeDelta::FromMinutes(1), + /* number_of_batched_sync_events= */ 10); + histogram_tester_.ExpectUniqueSample( + "PeriodicBackgroundSync.Event.Time", + base::TimeDelta::FromMinutes(1).InMilliseconds(), 1); +} + +TEST_F(BackgroundSyncMetricsTest, CountRegisterSuccess) { + BackgroundSyncMetrics::CountRegisterSuccess( + BackgroundSyncType::ONE_SHOT, + /* min_interval_ms= */ -1, BackgroundSyncMetrics::REGISTRATION_COULD_FIRE, + /* registration_is_duplicate= */ + BackgroundSyncMetrics::REGISTRATION_IS_NOT_DUPLICATE); + histogram_tester_.ExpectUniqueSample( + "BackgroundSync.Registration.OneShot.CouldFire", 1, 1); + histogram_tester_.ExpectUniqueSample("BackgroundSync.Registration.OneShot", + BACKGROUND_SYNC_STATUS_OK, 1); + histogram_tester_.ExpectUniqueSample( + "BackgroundSync.Registration.OneShot.IsDuplicate", 0, 1); + + BackgroundSyncMetrics::CountRegisterSuccess( + BackgroundSyncType::PERIODIC, + /* min_interval_ms= */ 1000, + BackgroundSyncMetrics::REGISTRATION_COULD_FIRE, + /* registration_is_duplicate= */ + BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); + histogram_tester_.ExpectUniqueSample( + "BackgroundSync.Registration.Periodic.MinInterval", 1, 1); + histogram_tester_.ExpectUniqueSample("BackgroundSync.Registration.Periodic", + BACKGROUND_SYNC_STATUS_OK, 1); + histogram_tester_.ExpectUniqueSample( + "BackgroundSync.Registration.Periodic.IsDuplicate", 1, 1); +} + +TEST_F(BackgroundSyncMetricsTest, CountUnregisterPeriodicSync) { + BackgroundSyncMetrics::CountUnregisterPeriodicSync(BACKGROUND_SYNC_STATUS_OK); + histogram_tester_.ExpectUniqueSample("BackgroundSync.Unregistration.Periodic", + BACKGROUND_SYNC_STATUS_OK, 1); +} + +} // namespace content
diff --git a/content/browser/browser_process_sub_thread.cc b/content/browser/browser_process_sub_thread.cc index e9d0f68..3c6e699 100644 --- a/content/browser/browser_process_sub_thread.cc +++ b/content/browser/browser_process_sub_thread.cc
@@ -200,8 +200,8 @@ // Record time spent for the method call. base::TimeDelta network_wait_time = base::TimeTicks::Now() - start_time; UMA_HISTOGRAM_TIMES("NetworkService.ShutdownTime", network_wait_time); - LOG(ERROR) << "Waited " << network_wait_time.InMilliseconds() - << " ms for network service"; + DVLOG(1) << "Waited " << network_wait_time.InMilliseconds() + << " ms for network service"; } }
diff --git a/content/browser/dom_storage/dom_storage_browsertest.cc b/content/browser/dom_storage/dom_storage_browsertest.cc index fe6046b..b240ee1 100644 --- a/content/browser/dom_storage/dom_storage_browsertest.cc +++ b/content/browser/dom_storage/dom_storage_browsertest.cc
@@ -9,7 +9,7 @@ #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/dom_storage/dom_storage_database.h" #include "content/browser/dom_storage/dom_storage_task_runner.h"
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h index 687c8c5b..309ec03 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.h +++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -14,7 +14,7 @@ #include "base/memory/weak_ptr.h" #include "base/synchronization/lock.h" #include "base/thread_annotations.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/common/content_export.h" #include "content/public/browser/dom_storage_context.h" #include "mojo/public/cpp/bindings/message.h"
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc index 24912064..22a16fb 100644 --- a/content/browser/dom_storage/local_storage_context_mojo.cc +++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -24,7 +24,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/dom_storage_database.h" #include "content/browser/dom_storage/local_storage_database.pb.h" #include "content/browser/dom_storage/storage_area_impl.h"
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h index 78dc38f8..9aea8a3 100644 --- a/content/browser/dom_storage/local_storage_context_mojo.h +++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -16,7 +16,7 @@ #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" #include "base/trace_event/memory_dump_provider.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/dom_storage/session_storage_area_impl_unittest.cc b/content/browser/dom_storage/session_storage_area_impl_unittest.cc index ff223e7c..ca5f602 100644 --- a/content/browser/dom_storage/session_storage_area_impl_unittest.cc +++ b/content/browser/dom_storage/session_storage_area_impl_unittest.cc
@@ -18,7 +18,7 @@ #include "base/threading/thread.h" #include "components/services/leveldb/leveldb_service_impl.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/session_storage_data_map.h" #include "content/browser/dom_storage/session_storage_metadata.h" #include "content/browser/dom_storage/test/storage_area_test_util.h"
diff --git a/content/browser/dom_storage/session_storage_context_mojo.cc b/content/browser/dom_storage/session_storage_context_mojo.cc index 4a0c263..7f43fdc7 100644 --- a/content/browser/dom_storage/session_storage_context_mojo.cc +++ b/content/browser/dom_storage/session_storage_context_mojo.cc
@@ -23,7 +23,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/session_storage_area_impl.h" #include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" #include "content/browser/dom_storage/storage_area_impl.h"
diff --git a/content/browser/dom_storage/session_storage_metadata.h b/content/browser/dom_storage/session_storage_metadata.h index c1a6b86..0d05204 100644 --- a/content/browser/dom_storage/session_storage_metadata.h +++ b/content/browser/dom_storage/session_storage_metadata.h
@@ -10,7 +10,7 @@ #include <vector> #include "base/memory/ref_counted.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/common/content_export.h" #include "url/origin.h"
diff --git a/content/browser/dom_storage/storage_area_impl.h b/content/browser/dom_storage/storage_area_impl.h index f44ef67..17ba5288 100644 --- a/content/browser/dom_storage/storage_area_impl.h +++ b/content/browser/dom_storage/storage_area_impl.h
@@ -15,7 +15,7 @@ #include "base/macros.h" #include "base/optional.h" #include "base/time/time.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/interface_ptr_set.h"
diff --git a/content/browser/dom_storage/storage_area_impl_unittest.cc b/content/browser/dom_storage/storage_area_impl_unittest.cc index 91ff0155..f4a1772 100644 --- a/content/browser/dom_storage/storage_area_impl_unittest.cc +++ b/content/browser/dom_storage/storage_area_impl_unittest.cc
@@ -15,7 +15,7 @@ #include "base/test/test_simple_task_runner.h" #include "base/threading/thread.h" #include "components/services/leveldb/public/cpp/util.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "content/browser/dom_storage/test/storage_area_test_util.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/test/barrier_builder.h"
diff --git a/content/browser/dom_storage/test/fake_leveldb_service.h b/content/browser/dom_storage/test/fake_leveldb_service.h index d7195bbd..4a683db 100644 --- a/content/browser/dom_storage/test/fake_leveldb_service.h +++ b/content/browser/dom_storage/test/fake_leveldb_service.h
@@ -10,7 +10,7 @@ #include "base/callback.h" #include "base/optional.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/file/public/mojom/file_system.mojom.h" #include "services/service_manager/public/cpp/bind_source_info.h"
diff --git a/content/browser/dom_storage/test/storage_area_test_util.h b/content/browser/dom_storage/test/storage_area_test_util.h index 6bdc724..e45d2a85 100644 --- a/content/browser/dom_storage/test/storage_area_test_util.h +++ b/content/browser/dom_storage/test/storage_area_test_util.h
@@ -9,7 +9,7 @@ #include "base/callback.h" #include "base/optional.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 4474a726..59829c1 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc
@@ -1086,6 +1086,8 @@ #if defined(OS_LINUX) int child_flags = gpu_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : ChildProcessHost::CHILD_NORMAL; +#elif defined(OS_MACOSX) + int child_flags = ChildProcessHost::CHILD_GPU; #else int child_flags = ChildProcessHost::CHILD_NORMAL; #endif
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_database.h b/content/browser/indexed_db/leveldb/transactional_leveldb_database.h index 621c9227..41314b0 100644 --- a/content/browser/indexed_db/leveldb/transactional_leveldb_database.h +++ b/content/browser/indexed_db/leveldb/transactional_leveldb_database.h
@@ -12,6 +12,7 @@ #include "base/files/file_path.h" #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" @@ -106,6 +107,10 @@ void SetClockForTesting(std::unique_ptr<base::Clock> clock); + base::WeakPtr<TransactionalLevelDBDatabase> AsWeakPtr() { + return weak_factory_for_iterators_.GetWeakPtr(); + } + private: friend class LevelDBSnapshot; friend class TransactionalLevelDBIteratorImpl; @@ -149,6 +154,9 @@ uint32_t max_iterators_ = 0; std::string file_name_for_tracing; + + base::WeakPtrFactory<TransactionalLevelDBDatabase> + weak_factory_for_iterators_{this}; }; } // namespace content
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.cc b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.cc index 32a9963..05dd0efb 100644 --- a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.cc +++ b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.cc
@@ -21,14 +21,15 @@ namespace content { TransactionalLevelDBIteratorImpl::~TransactionalLevelDBIteratorImpl() { - db_->OnIteratorDestroyed(this); + if (db_) + db_->OnIteratorDestroyed(this); } TransactionalLevelDBIteratorImpl::TransactionalLevelDBIteratorImpl( std::unique_ptr<leveldb::Iterator> it, TransactionalLevelDBDatabase* db, const leveldb::Snapshot* snapshot) - : iterator_(std::move(it)), db_(db), snapshot_(snapshot) {} + : iterator_(std::move(it)), db_(db->AsWeakPtr()), snapshot_(snapshot) {} leveldb::Status TransactionalLevelDBIteratorImpl::CheckStatus() { DCHECK(!IsDetached());
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.h b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.h index 2db939b5..cbb87395 100644 --- a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.h +++ b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator_impl.h
@@ -9,6 +9,7 @@ #include <string> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h" #include "content/common/content_export.h" #include "third_party/leveldatabase/src/include/leveldb/iterator.h" @@ -57,8 +58,9 @@ std::unique_ptr<leveldb::Iterator> iterator_; - // State used to facilitate memory purging. - TransactionalLevelDBDatabase* db_; + // State used to facilitate memory purging. Sometimes this is destroyed before + // we are, so use a WeakPtr. + base::WeakPtr<TransactionalLevelDBDatabase> db_; IteratorState iterator_state_ = IteratorState::ACTIVE; std::string key_before_eviction_; const leveldb::Snapshot* snapshot_;
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc index a1e1853..2406881 100644 --- a/content/browser/service_worker/service_worker_metrics.cc +++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -339,13 +339,6 @@ } } -void ServiceWorkerMetrics::RecordInstalledScriptsSenderStatus( - ServiceWorkerInstalledScriptReader::FinishedReason reason) { - UMA_HISTOGRAM_ENUMERATION( - "ServiceWorker.StartWorker.InstalledScriptsSender.FinishedReason", reason, - ServiceWorkerInstalledScriptReader::FinishedReason::kMaxValue); -} - void ServiceWorkerMetrics::RecordStartWorkerTime(base::TimeDelta time, bool is_installed, StartSituation start_situation,
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h index 6899c889..05def3e 100644 --- a/content/browser/service_worker/service_worker_metrics.h +++ b/content/browser/service_worker/service_worker_metrics.h
@@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/time/time.h" #include "content/browser/service_worker/service_worker_database.h" -#include "content/browser/service_worker/service_worker_installed_script_reader.h" #include "content/public/browser/service_worker_context.h" #include "content/public/common/resource_type.h" #include "services/network/public/mojom/fetch_api.mojom.h" @@ -211,10 +210,6 @@ blink::ServiceWorkerStatusCode status, EventType purpose); - // Records the result of sending installed scripts to the renderer. - static void RecordInstalledScriptsSenderStatus( - ServiceWorkerInstalledScriptReader::FinishedReason reason); - // Records the time taken to successfully start a worker. |is_installed| // indicates whether the version has been installed. //
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 3075547..7235a21 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -1798,11 +1798,6 @@ if (context_ && IsInstalled(prestart_status)) context_->UpdateVersionFailureCount(version_id_, status); - if (installed_scripts_sender_) { - ServiceWorkerMetrics::RecordInstalledScriptsSenderStatus( - installed_scripts_sender_->last_finished_reason()); - } - if (IsInstalled(prestart_status)) ServiceWorkerMetrics::RecordStartInstalledWorkerStatus(status, purpose);
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index 2a1f767..7485b5b 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc
@@ -495,10 +495,14 @@ true}, {"suggestionPicker.css", IDR_SUGGESTION_PICKER_CSS, ui::SCALE_FACTOR_NONE, true}, + {"color_picker_common.js", IDR_COLOR_PICKER_COMMON_JS, + ui::SCALE_FACTOR_NONE, true}, {"colorSuggestionPicker.js", IDR_COLOR_SUGGESTION_PICKER_JS, ui::SCALE_FACTOR_NONE, true}, {"colorSuggestionPicker.css", IDR_COLOR_SUGGESTION_PICKER_CSS, ui::SCALE_FACTOR_NONE, true}, + {"color_picker.js", IDR_COLOR_PICKER_JS, ui::SCALE_FACTOR_NONE, true}, + {"color_picker.css", IDR_COLOR_PICKER_CSS, ui::SCALE_FACTOR_NONE, true}, #endif {"input_alert.svg", IDR_VALIDATION_BUBBLE_ICON, ui::SCALE_FACTOR_NONE, true},
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 1e7646f..dc67a5f 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -504,7 +504,7 @@ import_dirs = [ "//mojo/services" ] public_deps = [ - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//components/tracing/common:interfaces", "//content/public/common:interfaces", "//content/public/common:resource_type_bindings",
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc index 47032522..05a66b8 100644 --- a/content/common/child_process_host_impl.cc +++ b/content/common/child_process_host_impl.cc
@@ -85,6 +85,8 @@ if (flags == CHILD_RENDERER) { child_base_name += kMacHelperSuffix_renderer; + } else if (flags == CHILD_GPU) { + child_base_name += kMacHelperSuffix_gpu; } else if (flags == CHILD_PLUGIN) { child_base_name += kMacHelperSuffix_plugin; } else {
diff --git a/content/public/app/mac_helpers.gni b/content/public/app/mac_helpers.gni index b7ada75..1414b742 100644 --- a/content/public/app/mac_helpers.gni +++ b/content/public/app/mac_helpers.gni
@@ -35,6 +35,14 @@ " (Renderer)", ], + # A helper with the capability to execute writable memory, needed by + # Swiftshader, and used to run renderer processes. + [ + "gpu", + ".gpu", + " (GPU)", + ], + # A helper that does not perform library validation, allowing code not signed # by either Apple or the signing identity to be loaded, and that can execute # unsigned memory. This is used by binary plugins like Flash.
diff --git a/content/public/common/child_process_host.h b/content/public/common/child_process_host.h index 63e27bb..e62da71f 100644 --- a/content/public/common/child_process_host.h +++ b/content/public/common/child_process_host.h
@@ -72,6 +72,12 @@ // into which the writable-and-executable data are stored. CHILD_RENDERER, + // Starts a child process with the macOS entitlement that allows unsigned + // executable memory. + // TODO(https://crbug.com/985816): Change this to use MAP_JIT and the + // allow-jit entitlement instead. + CHILD_GPU, + // Starts a child process with the macOS entitlement that ignores the // library validation code signing enforcement. Library validation mandates // that all executable pages be backed by a code signature that either 1)
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index d019d88..f31ac49 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc
@@ -113,6 +113,7 @@ #include "services/network/public/mojom/network_service_test.mojom.h" #include "services/service_manager/public/cpp/connector.h" #include "storage/browser/fileapi/file_system_context.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/filesystem/file_system.mojom.h" #include "ui/base/clipboard/clipboard.h" @@ -127,6 +128,14 @@ #include "ui/latency/latency_info.h" #include "ui/resources/grit/webui_resources.h" +#if defined(OS_WIN) +#include <uiautomation.h> +#include <wrl/client.h> +#include "base/win/atl.h" +#include "base/win/scoped_safearray.h" +#include "base/win/scoped_variant.h" +#endif + #if defined(USE_AURA) #include "content/browser/renderer_host/overscroll_controller.h" #include "content/browser/renderer_host/render_widget_host_view_aura.h" @@ -2001,6 +2010,64 @@ return nullptr; } +#if defined(OS_WIN) +template <typename T> +Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode( + BrowserAccessibility* browser_accessibility) { + Microsoft::WRL::ComPtr<T> result; + EXPECT_HRESULT_SUCCEEDED( + browser_accessibility->GetNativeViewAccessible()->QueryInterface( + __uuidof(T), &result)); + return result; +} + +void UiaGetPropertyValueVtArrayVtUnknownValidate( + PROPERTYID property_id, + BrowserAccessibility* target_browser_accessibility, + const std::vector<std::string>& expected_names) { + ASSERT_NE(nullptr, target_browser_accessibility); + + base::win::ScopedVariant result_variant; + Microsoft::WRL::ComPtr<IRawElementProviderSimple> node_provider = + QueryInterfaceFromNode<IRawElementProviderSimple>( + target_browser_accessibility); + + node_provider->GetPropertyValue(property_id, result_variant.Receive()); + ASSERT_EQ(VT_ARRAY | VT_UNKNOWN, result_variant.type()); + ASSERT_EQ(1u, SafeArrayGetDim(V_ARRAY(result_variant.ptr()))); + + LONG lower_bound, upper_bound, size; + ASSERT_HRESULT_SUCCEEDED( + SafeArrayGetLBound(V_ARRAY(result_variant.ptr()), 1, &lower_bound)); + ASSERT_HRESULT_SUCCEEDED( + SafeArrayGetUBound(V_ARRAY(result_variant.ptr()), 1, &upper_bound)); + size = upper_bound - lower_bound + 1; + ASSERT_EQ(static_cast<LONG>(expected_names.size()), size); + + std::vector<std::string> names; + for (LONG i = 0; i < size; ++i) { + CComPtr<IUnknown> unknown_element = nullptr; + ASSERT_HRESULT_SUCCEEDED(SafeArrayGetElement(V_ARRAY(result_variant.ptr()), + &i, &unknown_element)); + ASSERT_NE(nullptr, unknown_element); + + CComPtr<IRawElementProviderSimple> raw_element_provider_simple = nullptr; + ASSERT_HRESULT_SUCCEEDED( + unknown_element->QueryInterface(&raw_element_provider_simple)); + ASSERT_NE(nullptr, raw_element_provider_simple); + + base::win::ScopedVariant name; + ASSERT_HRESULT_SUCCEEDED(raw_element_provider_simple->GetPropertyValue( + UIA_NamePropertyId, name.Receive())); + ASSERT_EQ(VT_BSTR, name.type()); + names.push_back(base::UTF16ToUTF8( + base::string16(V_BSTR(name.ptr()), SysStringLen(V_BSTR(name.ptr()))))); + } + + ASSERT_THAT(names, testing::UnorderedElementsAreArray(expected_names)); +} +#endif + bool IsWebContentsBrowserPluginFocused(content::WebContents* web_contents) { WebContentsImpl* web_contents_impl = static_cast<WebContentsImpl*>(web_contents);
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h index 0e342cc..26333783 100644 --- a/content/public/test/browser_test_utils.h +++ b/content/public/test/browser_test_utils.h
@@ -69,6 +69,17 @@ using test_server::EmbeddedTestServer; } +#if defined(OS_WIN) +namespace Microsoft { +namespace WRL { +template <typename> +class ComPtr; +} // namespace WRL +} // namespace Microsoft + +typedef int PROPERTYID; +#endif + // A collections of functions designed for use with content_browsertests and // browser_tests. // TO BE CLEAR: any function here must work against both binaries. If it only @@ -888,6 +899,22 @@ BrowserAccessibility* node, const FindAccessibilityNodeCriteria& criteria); +#if defined(OS_WIN) +// Retrieve the specified interface from an accessibility node. +template <typename T> +Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode( + BrowserAccessibility* browser_accessibility); + +// Call GetPropertyValue with the given UIA property id with variant type +// VT_ARRAY | VT_UNKNOWN on the target browser accessibility node to retrieve +// an array of automation elements, then validate the name property of the +// automation elements with the expected names. +void UiaGetPropertyValueVtArrayVtUnknownValidate( + PROPERTYID property_id, + BrowserAccessibility* target_browser_accessibility, + const std::vector<std::string>& expected_names); +#endif + // Find out if the BrowserPlugin for a guest WebContents is focused. Returns // false if the WebContents isn't a guest with a BrowserPlugin. bool IsWebContentsBrowserPluginFocused(content::WebContents* web_contents);
diff --git a/content/renderer/input/input_event_prediction.cc b/content/renderer/input/input_event_prediction.cc index 17bfa4b..e504a73f 100644 --- a/content/renderer/input/input_event_prediction.cc +++ b/content/renderer/input/input_event_prediction.cc
@@ -70,6 +70,7 @@ base::TimeTicks predict_time = coalesced_event.CoalescedEvent(coalesced_size - 1).TimeStamp() + kPredictionInterval; + for (uint32_t i = 0; i < kPredictEventCount; i++) { if (!AddPredictedEvent(predict_time, coalesced_event)) break; @@ -114,21 +115,26 @@ void InputEventPrediction::ApplyResampling(base::TimeTicks frame_time, WebInputEvent* event) { + // When resampling, we don't want to predict too far away because the + // result will likely be inaccurate in that case. We then cut off the + // prediction to the maximum available for the current mouse predictor + base::TimeDelta prediction_delta = std::min( + frame_time - event->TimeStamp(), mouse_predictor_->MaxResampleTime()); + base::TimeTicks predict_time = event->TimeStamp() + prediction_delta; + if (event->GetType() == WebInputEvent::kTouchMove) { WebTouchEvent* touch_event = static_cast<WebTouchEvent*>(event); for (unsigned i = 0; i < touch_event->touches_length; ++i) { - if (GetPointerPrediction(frame_time, &touch_event->touches[i], - true /*is_resampling*/)) - event->SetTimeStamp(frame_time); + if (GetPointerPrediction(predict_time, &touch_event->touches[i])) + event->SetTimeStamp(predict_time); } } else if (event->GetType() == WebInputEvent::kMouseMove) { - if (GetPointerPrediction(frame_time, static_cast<WebMouseEvent*>(event), - true /*is_resampling*/)) - event->SetTimeStamp(frame_time); + if (GetPointerPrediction(predict_time, static_cast<WebMouseEvent*>(event))) + event->SetTimeStamp(predict_time); } else if (event->GetType() == WebInputEvent::kPointerMove) { - if (GetPointerPrediction(frame_time, static_cast<WebPointerEvent*>(event), - true /*is_resampling*/)) - event->SetTimeStamp(frame_time); + if (GetPointerPrediction(predict_time, + static_cast<WebPointerEvent*>(event))) + event->SetTimeStamp(predict_time); } } @@ -198,13 +204,11 @@ } bool InputEventPrediction::GetPointerPrediction(base::TimeTicks predict_time, - WebPointerProperties* event, - bool is_resampling) { + WebPointerProperties* event) { ui::InputPredictor::InputData predict_result; if (event->pointer_type == WebPointerProperties::PointerType::kMouse) { if (mouse_predictor_->HasPrediction() && - mouse_predictor_->GeneratePrediction(predict_time, is_resampling, - &predict_result)) { + mouse_predictor_->GeneratePrediction(predict_time, &predict_result)) { event->SetPositionInWidget(predict_result.pos); return true; } @@ -215,8 +219,7 @@ auto predictor = pointer_id_predictor_map_.find(event->id); if (predictor != pointer_id_predictor_map_.end() && predictor->second->HasPrediction() && - predictor->second->GeneratePrediction(predict_time, is_resampling, - &predict_result)) { + predictor->second->GeneratePrediction(predict_time, &predict_result)) { event->SetPositionInWidget(predict_result.pos); return true; } @@ -255,7 +258,6 @@ if (predictor != pointer_id_predictor_map_.end() && predictor->second->HasPrediction() && predictor->second->GeneratePrediction(event.TimeStamp(), - false /* is_resampling */, &predict_result)) { float distance = (predict_result.pos - @@ -270,8 +272,8 @@ } else if (event.GetType() == WebInputEvent::kMouseMove) { const WebMouseEvent& mouse_event = static_cast<const WebMouseEvent&>(event); if (mouse_predictor_->HasPrediction() && - mouse_predictor_->GeneratePrediction( - event.TimeStamp(), false /* is_resampling */, &predict_result)) { + mouse_predictor_->GeneratePrediction(event.TimeStamp(), + &predict_result)) { float distance = (predict_result.pos - gfx::PointF(mouse_event.PositionInWidget())) .Length();
diff --git a/content/renderer/input/input_event_prediction.h b/content/renderer/input/input_event_prediction.h index 553d23a..10a2ccc 100644 --- a/content/renderer/input/input_event_prediction.h +++ b/content/renderer/input/input_event_prediction.h
@@ -44,6 +44,8 @@ friend class InputEventPredictionTest; FRIEND_TEST_ALL_PREFIXES(InputEventPredictionTest, PredictorType); FRIEND_TEST_ALL_PREFIXES(InputEventPredictionTest, ResamplingDisabled); + FRIEND_TEST_ALL_PREFIXES(InputEventPredictionTest, + NoResampleWhenExceedMaxResampleTime); // The following functions are for handling multiple TouchPoints in a // WebTouchEvent. They should be more neat when WebTouchEvent is elimated. @@ -69,8 +71,7 @@ // and apply predicted result to the event. Return false if no prediction // available. bool GetPointerPrediction(base::TimeTicks predict_time, - WebPointerProperties* event, - bool is_resampling = false); + WebPointerProperties* event); // Get single predictor based on event id and type. For mouse, reset the // predictor, for other pointer type, remove it from mapping.
diff --git a/content/renderer/input/input_event_prediction_unittest.cc b/content/renderer/input/input_event_prediction_unittest.cc index 9f5a1170..6da7bad0 100644 --- a/content/renderer/input/input_event_prediction_unittest.cc +++ b/content/renderer/input/input_event_prediction_unittest.cc
@@ -44,16 +44,14 @@ if (event.pointer_type == WebPointerProperties::PointerType::kMouse) { return event_predictor_->mouse_predictor_->GeneratePrediction( - WebInputEvent::GetStaticTimeStampForTests(), - false /* is_resampling */, result); + ui::EventTimeForNow(), result); } else { auto predictor = event_predictor_->pointer_id_predictor_map_.find(event.id); if (predictor != event_predictor_->pointer_id_predictor_map_.end()) - return predictor->second->GeneratePrediction( - WebInputEvent::GetStaticTimeStampForTests(), - false /* is_resampling */, result); + return predictor->second->GeneratePrediction(ui::EventTimeForNow(), + result); else return false; } @@ -61,8 +59,7 @@ void HandleEvents(const WebInputEvent& event) { blink::WebCoalescedInputEvent coalesced_event(event); - event_predictor_->HandleEvents(coalesced_event, - WebInputEvent::GetStaticTimeStampForTests()); + event_predictor_->HandleEvents(coalesced_event, ui::EventTimeForNow()); } void ConfigureFieldTrial(const base::Feature& feature, @@ -146,6 +143,7 @@ WebMouseEvent mouse_down = SyntheticWebMouseEventBuilder::Build( WebInputEvent::kMouseDown, 10, 10, 0); + HandleEvents(mouse_down); EXPECT_FALSE(GetPrediction(mouse_down, &last_point)); } @@ -158,6 +156,7 @@ touch_event.PressPoint(10, 10); touch_event.touches[0].pointer_type = WebPointerProperties::PointerType::kTouch; + HandleEvents(touch_event); EXPECT_FALSE(GetPrediction(touch_event.touches[0], &last_point)); @@ -189,6 +188,7 @@ WebMouseEvent pen_leave = SyntheticWebMouseEventBuilder::Build( WebInputEvent::kMouseLeave, 10, 10, 0, WebPointerProperties::PointerType::kPen); + HandleEvents(pen_leave); EXPECT_EQ(GetPredictorMapSize(), 0); EXPECT_FALSE(GetPrediction(pen_leave, &last_point)); @@ -202,6 +202,7 @@ touch_event.MovePoint(0, 11, 12); touch_event.touches[0].pointer_type = WebPointerProperties::PointerType::kTouch; + HandleEvents(touch_event); // Press 2nd touch point @@ -232,6 +233,7 @@ TEST_F(InputEventPredictionTest, TouchAndStylusResetMousePredictor) { WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build( WebInputEvent::kMouseMove, 10, 10, 0); + HandleEvents(mouse_move); ui::InputPredictor::InputData last_point; EXPECT_TRUE(GetPrediction(mouse_move, &last_point)); @@ -240,6 +242,7 @@ WebInputEvent::kMouseMove, 20, 20, 0, WebPointerProperties::PointerType::kPen); pen_move.id = 1; + HandleEvents(pen_move); EXPECT_TRUE(GetPrediction(pen_move, &last_point)); EXPECT_FALSE(GetPrediction(mouse_move, &last_point)); @@ -251,6 +254,7 @@ touch_event.PressPoint(10, 10); touch_event.touches[0].pointer_type = WebPointerProperties::PointerType::kTouch; + HandleEvents(touch_event); touch_event.MovePoint(0, 10, 10); HandleEvents(touch_event); @@ -292,6 +296,7 @@ // Send 3 mouse move to get kalman predictor ready. WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build( WebInputEvent::kMouseMove, 10, 10, 0); + HandleEvents(mouse_move); mouse_move = SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseMove, 11, 9, 0); @@ -317,12 +322,15 @@ EXPECT_EQ(event.PositionInWidget().y, 7); } -// Test that when dt > 20ms, no resampling, but has predicted points. +// Test that when dt > maxResampling, resampling is cut off . TEST_F(InputEventPredictionTest, NoResampleWhenExceedMaxResampleTime) { ConfigureFieldTrialAndInitialize( features::kResamplingInputEvents, ui::input_prediction::kScrollPredictorNameKalman); + base::TimeDelta predictor_max_resample_time = + event_predictor_->mouse_predictor_->MaxResampleTime(); + base::TimeTicks event_time = ui::EventTimeForNow(); // Send 3 mouse move each has 8ms interval to get kalman predictor ready. WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build( @@ -346,7 +354,7 @@ mouse_move.SetTimeStamp(event_time += base::TimeDelta::FromMilliseconds(8)); blink::WebCoalescedInputEvent coalesced_event(mouse_move); base::TimeTicks frame_time = - event_time + base::TimeDelta::FromMilliseconds(8); + event_time + predictor_max_resample_time; // No cut off event_predictor_->HandleEvents(coalesced_event, frame_time); const WebMouseEvent& event = @@ -362,21 +370,25 @@ } { - // When frame time is 20ms away from the event, no resampling, but still - // have 3 predicted events. + // Test When the delta time between the frame time and the event is greater + // than the maximum resampling time for a predictor, the resampling is cut + // off to the maximum allowed by the predictor mouse_move = SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseMove, 14, 6, 0); mouse_move.SetTimeStamp(event_time += base::TimeDelta::FromMilliseconds(8)); blink::WebCoalescedInputEvent coalesced_event(mouse_move); base::TimeTicks frame_time = - event_time + base::TimeDelta::FromMilliseconds(21); + event_time + predictor_max_resample_time + + base::TimeDelta::FromMilliseconds(10); // overpredict on purpose event_predictor_->HandleEvents(coalesced_event, frame_time); + // We expect the prediction to be cut off to the max resampling time of + // the predictor const WebMouseEvent& event = static_cast<const blink::WebMouseEvent&>(coalesced_event.Event()); - EXPECT_EQ(event.PositionInWidget().x, 14); - EXPECT_EQ(event.PositionInWidget().y, 6); - EXPECT_EQ(event.TimeStamp(), event_time); + EXPECT_GT(event.PositionInWidget().x, 14); + EXPECT_LT(event.PositionInWidget().y, 6); + EXPECT_EQ(event.TimeStamp(), event_time + predictor_max_resample_time); EXPECT_EQ(coalesced_event.PredictedEventSize(), 3u); // First predicted event time stamp is 8ms from original event timestamp.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index ab15e71a..7d3f8cf 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -360,7 +360,7 @@ ":content_test_mojo_bindings", "//base/third_party/dynamic_annotations", "//cc:test_support", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//components/viz/host", "//components/viz/service", "//content/app:both_for_content_tests", @@ -1510,6 +1510,7 @@ "../browser/background_fetch/storage/image_helpers_unittest.cc", "../browser/background_sync/background_sync_launcher_unittest.cc", "../browser/background_sync/background_sync_manager_unittest.cc", + "../browser/background_sync/background_sync_metrics_unittest.cc", "../browser/background_sync/background_sync_network_observer_unittest.cc", "../browser/background_sync/background_sync_service_impl_test_harness.cc", "../browser/background_sync/background_sync_service_impl_test_harness.h",
diff --git a/content/test/fake_leveldb_database.h b/content/test/fake_leveldb_database.h index 92266b9..ccea0eb 100644 --- a/content/test/fake_leveldb_database.h +++ b/content/test/fake_leveldb_database.h
@@ -6,7 +6,7 @@ #define CONTENT_TEST_FAKE_LEVELDB_DATABASE_H_ #include "base/memory/ref_counted.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" namespace content {
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md index d4ab78e2..927931f8 100644 --- a/docs/android_build_instructions.md +++ b/docs/android_build_instructions.md
@@ -158,7 +158,7 @@ *** promo `arm` and `x86` may optionally be used instead of `arm64` and `x64` for non-WebView targets. This is also allowed for Monochrome, but only when not set -as WebView the provider. +as the WebView provider. *** ## Build Chromium
diff --git a/docs/security/severity-guidelines.md b/docs/security/severity-guidelines.md index 3937ba83..1ce2b1e 100644 --- a/docs/security/severity-guidelines.md +++ b/docs/security/severity-guidelines.md
@@ -50,7 +50,8 @@ ## High severity {#TOC-High-severity} High severity vulnerabilities allow an attacker to execute code in the context -of, or otherwise impersonate other origins. Bugs which would normally be +of, or otherwise impersonate other origins or read cross-origin data. +Bugs which would normally be critical severity with unusual mitigating factors may be rated as high severity. For example, renderer sandbox escapes fall into this category as their impact is that of a critical severity bug, but they require the precondition of a @@ -81,6 +82,8 @@ compromised renderer ([377392](https://crbug.com/377392)). * Memory corruption in the browser process that requires specific user interaction, such as granting a permission ([455735](https://crbug.com/455735)). +* Cross-site execution contexts unexpectedly sharing a renderer process despite +Site Isolation ([863069](https://crbug.com/863069)). ## Medium severity {#TOC-Medium-severity}
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc index 49a67a1..229787d 100644 --- a/extensions/browser/api/storage/storage_frontend.cc +++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -12,7 +12,6 @@ #include "base/json/json_reader.h" #include "base/lazy_instance.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "content/public/browser/browser_context.h" @@ -132,7 +131,6 @@ void StorageFrontend::Init(scoped_refptr<ValueStoreFactory> factory) { TRACE_EVENT0("browser,startup", "StorageFrontend::Init") - SCOPED_UMA_HISTOGRAM_TIMER("Extensions.StorageFrontendInitTime"); observers_ = new SettingsObserverList(); browser_context_observer_.reset(new DefaultObserver(browser_context_));
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc index 7bcfd36..7a04e76 100644 --- a/extensions/browser/extension_prefs.cc +++ b/extensions/browser/extension_prefs.cc
@@ -1658,7 +1658,6 @@ void ExtensionPrefs::InitPrefStore() { TRACE_EVENT0("browser,startup", "ExtensionPrefs::InitPrefStore") - SCOPED_UMA_HISTOGRAM_TIMER("Extensions.InitPrefStoreTime"); // When this is called, the PrefService is initialized and provides access // to the user preferences stored in a JSON file. @@ -2000,7 +1999,6 @@ const ExtensionsInfo& extensions_info) { TRACE_EVENT0("browser,startup", "ExtensionPrefs::InitExtensionControlledPrefs") - SCOPED_UMA_HISTOGRAM_TIMER("Extensions.InitExtensionControlledPrefsTime"); for (const auto& info : extensions_info) { const ExtensionId& extension_id = info->extension_id;
diff --git a/extensions/shell/browser/shell_nacl_browser_delegate.cc b/extensions/shell/browser/shell_nacl_browser_delegate.cc index c196936..ed4cfbc 100644 --- a/extensions/shell/browser/shell_nacl_browser_delegate.cc +++ b/extensions/shell/browser/shell_nacl_browser_delegate.cc
@@ -112,8 +112,9 @@ bool ShellNaClBrowserDelegate::MapUrlToLocalFilePath( const GURL& file_url, bool use_blocking_api, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) { + ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_); DCHECK(extension_system); // Check that the URL is recognized by the extension system. @@ -152,14 +153,9 @@ } bool ShellNaClBrowserDelegate::IsNonSfiModeAllowed( - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, const GURL& manifest_url) { return false; } -extensions::ExtensionSystem* ShellNaClBrowserDelegate::GetExtensionSystem( - const base::FilePath& profile_directory) { - return ExtensionSystem::Get(browser_context_); -} - } // namespace extensions
diff --git a/extensions/shell/browser/shell_nacl_browser_delegate.h b/extensions/shell/browser/shell_nacl_browser_delegate.h index b19eeef..80907be 100644 --- a/extensions/shell/browser/shell_nacl_browser_delegate.h +++ b/extensions/shell/browser/shell_nacl_browser_delegate.h
@@ -37,14 +37,12 @@ content::BrowserPpapiHost* ppapi_host) override; bool MapUrlToLocalFilePath(const GURL& url, bool is_blocking, - extensions::ExtensionSystem* extension_system, + const base::FilePath& profile_directory, base::FilePath* file_path) override; void SetDebugPatterns(const std::string& debug_patterns) override; bool URLMatchesDebugPatterns(const GURL& manifest_url) override; - bool IsNonSfiModeAllowed(extensions::ExtensionSystem* extension_system, + bool IsNonSfiModeAllowed(const base::FilePath& profile_directory, const GURL& manifest_url) override; - extensions::ExtensionSystem* GetExtensionSystem( - const base::FilePath& profile_directory) override; private: content::BrowserContext* browser_context_; // Not owned.
diff --git a/fuchsia/engine/web_engine_integration_tests.cmx b/fuchsia/engine/web_engine_integration_tests.cmx index 9b07cb5..3c0c9ca 100644 --- a/fuchsia/engine/web_engine_integration_tests.cmx +++ b/fuchsia/engine/web_engine_integration_tests.cmx
@@ -7,6 +7,7 @@ "deprecated-ambient-replace-as-executable" ], "services": [ + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", "fuchsia.logger.LogSink",
diff --git a/fuchsia/http/http.cmx b/fuchsia/http/http.cmx index 8e047c54..b4cde0cf 100644 --- a/fuchsia/http/http.cmx +++ b/fuchsia/http/http.cmx
@@ -5,6 +5,7 @@ "deprecated-ambient-replace-as-executable" ], "services": [ + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.device.NameProvider", "fuchsia.logger.LogSink", "fuchsia.net.NameLookup",
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx index 436200d..cdb498d 100644 --- a/fuchsia/runners/cast/cast_runner.cmx +++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -5,6 +5,7 @@ ], "services": [ "chromium.cast.ApplicationConfigManager", + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", "fuchsia.logger.LogSink",
diff --git a/fuchsia/runners/web/web_runner.cmx b/fuchsia/runners/web/web_runner.cmx index 40eb67f..83648b53 100644 --- a/fuchsia/runners/web/web_runner.cmx +++ b/fuchsia/runners/web/web_runner.cmx
@@ -5,6 +5,7 @@ "deprecated-ambient-replace-as-executable" ], "services": [ + "fuchsia.deprecatedtimezone.Timezone", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", "fuchsia.logger.LogSink",
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h index 958806e..1c79e0d 100644 --- a/gpu/GLES2/gl2extchromium.h +++ b/gpu/GLES2/gl2extchromium.h
@@ -779,7 +779,7 @@ glScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform); GL_APICALL void GL_APIENTRY
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 372ca08..ccf8729 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -3905,7 +3905,7 @@ 'impl_func': False, 'client_test': False, 'cmd_args': 'GLfloat opacity, GLboolean is_clipped, ' - 'GLfloat clip_rect_corner_radius, GLint sorting_context_id, ' + 'GLint sorting_context_id, ' 'GLuint shm_id, GLuint shm_offset', 'extension': 'CHROMIUM_schedule_ca_layer', },
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index ecb36c8..8f261f4 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1577,12 +1577,12 @@ GLES2ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) { gles2::GetGLContext()->ScheduleCALayerSharedStateCHROMIUM( - opacity, is_clipped, clip_rect, clip_rect_corner_radius, - sorting_context_id, transform); + opacity, is_clipped, clip_rect, rounded_corner_bounds, sorting_context_id, + transform); } void GL_APIENTRY GLES2ScheduleCALayerCHROMIUM(GLuint contents_texture_id, const GLfloat* contents_rect,
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index 48b8023..cc930c0 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2949,15 +2949,13 @@ void ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, - GLfloat clip_rect_corner_radius, GLint sorting_context_id, GLuint shm_id, GLuint shm_offset) { gles2::cmds::ScheduleCALayerSharedStateCHROMIUM* c = GetCmdSpace<gles2::cmds::ScheduleCALayerSharedStateCHROMIUM>(); if (c) { - c->Init(opacity, is_clipped, clip_rect_corner_radius, sorting_context_id, - shm_id, shm_offset); + c->Init(opacity, is_clipped, sorting_context_id, shm_id, shm_offset); } }
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index a982192..5703d8c 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -5397,10 +5397,11 @@ GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) { - uint32_t shm_size = 20 * sizeof(GLfloat); + // 4 for clip_rect, 5 for rounded_corner_rect, 16 for transform. + uint32_t shm_size = 25 * sizeof(GLfloat); ScopedTransferBufferPtr buffer(shm_size, helper_, transfer_buffer_); if (!buffer.valid() || buffer.size() < shm_size) { SetGLError(GL_OUT_OF_MEMORY, "GLES2::ScheduleCALayerSharedStateCHROMIUM", @@ -5409,10 +5410,11 @@ } GLfloat* mem = static_cast<GLfloat*>(buffer.address()); memcpy(mem + 0, clip_rect, 4 * sizeof(GLfloat)); - memcpy(mem + 4, transform, 16 * sizeof(GLfloat)); - helper_->ScheduleCALayerSharedStateCHROMIUM( - opacity, is_clipped, clip_rect_corner_radius, sorting_context_id, - buffer.shm_id(), buffer.offset()); + memcpy(mem + 4, rounded_corner_bounds, 5 * sizeof(GLfloat)); + memcpy(mem + 9, transform, 16 * sizeof(GLfloat)); + helper_->ScheduleCALayerSharedStateCHROMIUM(opacity, is_clipped, + sorting_context_id, + buffer.shm_id(), buffer.offset()); } void GLES2Implementation::ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index 64ec205..af1eb25 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1113,7 +1113,7 @@ void ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) override;
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index ea468e3..2ae7aef 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -825,12 +825,13 @@ GLfloat uv_height, GLboolean enable_blend, GLuint gpu_fence_id) = 0; -virtual void ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, - GLboolean is_clipped, - const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, - GLint sorting_context_id, - const GLfloat* transform) = 0; +virtual void ScheduleCALayerSharedStateCHROMIUM( + GLfloat opacity, + GLboolean is_clipped, + const GLfloat* clip_rect, + const GLfloat* rounded_corner_bounds, + GLint sorting_context_id, + const GLfloat* transform) = 0; virtual void ScheduleCALayerCHROMIUM(GLuint contents_texture_id, const GLfloat* contents_rect, GLuint background_color,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index 55e2c9f06..4951913 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -805,7 +805,7 @@ void ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) override; void ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index c6af9dbe..9ed76ef 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1077,7 +1077,7 @@ GLfloat /* opacity */, GLboolean /* is_clipped */, const GLfloat* /* clip_rect */, - GLfloat /* clip_rect_corner_radius */, + const GLfloat* /* rounded_corner_bounds */, GLint /* sorting_context_id */, const GLfloat* /* transform */) {} void GLES2InterfaceStub::ScheduleCALayerCHROMIUM(
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index aa9b4ef8..033b9b60 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -805,7 +805,7 @@ void ScheduleCALayerSharedStateCHROMIUM(GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) override; void ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index caab6ae5..1b49260 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2265,13 +2265,13 @@ GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) { TRACE_EVENT_BINARY_EFFICIENT0( "gpu", "GLES2Trace::ScheduleCALayerSharedStateCHROMIUM"); gl_->ScheduleCALayerSharedStateCHROMIUM(opacity, is_clipped, clip_rect, - clip_rect_corner_radius, + rounded_corner_bounds, sorting_context_id, transform); }
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h index 2ea1ad4..01e551a8 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -14582,14 +14582,12 @@ void Init(GLfloat _opacity, GLboolean _is_clipped, - GLfloat _clip_rect_corner_radius, GLint _sorting_context_id, GLuint _shm_id, GLuint _shm_offset) { SetHeader(); opacity = _opacity; is_clipped = _is_clipped; - clip_rect_corner_radius = _clip_rect_corner_radius; sorting_context_id = _sorting_context_id; shm_id = _shm_id; shm_offset = _shm_offset; @@ -14598,27 +14596,24 @@ void* Set(void* cmd, GLfloat _opacity, GLboolean _is_clipped, - GLfloat _clip_rect_corner_radius, GLint _sorting_context_id, GLuint _shm_id, GLuint _shm_offset) { static_cast<ValueType*>(cmd)->Init( - _opacity, _is_clipped, _clip_rect_corner_radius, _sorting_context_id, - _shm_id, _shm_offset); + _opacity, _is_clipped, _sorting_context_id, _shm_id, _shm_offset); return NextCmdAddress<ValueType>(cmd); } gpu::CommandHeader header; float opacity; uint32_t is_clipped; - float clip_rect_corner_radius; int32_t sorting_context_id; uint32_t shm_id; uint32_t shm_offset; }; -static_assert(sizeof(ScheduleCALayerSharedStateCHROMIUM) == 28, - "size of ScheduleCALayerSharedStateCHROMIUM should be 28"); +static_assert(sizeof(ScheduleCALayerSharedStateCHROMIUM) == 24, + "size of ScheduleCALayerSharedStateCHROMIUM should be 24"); static_assert( offsetof(ScheduleCALayerSharedStateCHROMIUM, header) == 0, "offset of ScheduleCALayerSharedStateCHROMIUM header should be 0"); @@ -14629,19 +14624,15 @@ offsetof(ScheduleCALayerSharedStateCHROMIUM, is_clipped) == 8, "offset of ScheduleCALayerSharedStateCHROMIUM is_clipped should be 8"); static_assert(offsetof(ScheduleCALayerSharedStateCHROMIUM, - clip_rect_corner_radius) == 12, - "offset of ScheduleCALayerSharedStateCHROMIUM " - "clip_rect_corner_radius should be 12"); -static_assert(offsetof(ScheduleCALayerSharedStateCHROMIUM, - sorting_context_id) == 16, + sorting_context_id) == 12, "offset of ScheduleCALayerSharedStateCHROMIUM sorting_context_id " - "should be 16"); + "should be 12"); static_assert( - offsetof(ScheduleCALayerSharedStateCHROMIUM, shm_id) == 20, - "offset of ScheduleCALayerSharedStateCHROMIUM shm_id should be 20"); + offsetof(ScheduleCALayerSharedStateCHROMIUM, shm_id) == 16, + "offset of ScheduleCALayerSharedStateCHROMIUM shm_id should be 16"); static_assert( - offsetof(ScheduleCALayerSharedStateCHROMIUM, shm_offset) == 24, - "offset of ScheduleCALayerSharedStateCHROMIUM shm_offset should be 24"); + offsetof(ScheduleCALayerSharedStateCHROMIUM, shm_offset) == 20, + "offset of ScheduleCALayerSharedStateCHROMIUM shm_offset should be 20"); struct ScheduleCALayerCHROMIUM { typedef ScheduleCALayerCHROMIUM ValueType;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h index 049e482..64a5457 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -4850,20 +4850,18 @@ TEST_F(GLES2FormatTest, ScheduleCALayerSharedStateCHROMIUM) { cmds::ScheduleCALayerSharedStateCHROMIUM& cmd = *GetBufferAs<cmds::ScheduleCALayerSharedStateCHROMIUM>(); - void* next_cmd = - cmd.Set(&cmd, static_cast<GLfloat>(11), static_cast<GLboolean>(12), - static_cast<GLfloat>(13), static_cast<GLint>(14), - static_cast<GLuint>(15), static_cast<GLuint>(16)); + void* next_cmd = cmd.Set(&cmd, static_cast<GLfloat>(11), + static_cast<GLboolean>(12), static_cast<GLint>(13), + static_cast<GLuint>(14), static_cast<GLuint>(15)); EXPECT_EQ( static_cast<uint32_t>(cmds::ScheduleCALayerSharedStateCHROMIUM::kCmdId), cmd.header.command); EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); EXPECT_EQ(static_cast<GLfloat>(11), cmd.opacity); EXPECT_EQ(static_cast<GLboolean>(12), cmd.is_clipped); - EXPECT_EQ(static_cast<GLfloat>(13), cmd.clip_rect_corner_radius); - EXPECT_EQ(static_cast<GLint>(14), cmd.sorting_context_id); - EXPECT_EQ(static_cast<GLuint>(15), cmd.shm_id); - EXPECT_EQ(static_cast<GLuint>(16), cmd.shm_offset); + EXPECT_EQ(static_cast<GLint>(13), cmd.sorting_context_id); + EXPECT_EQ(static_cast<GLuint>(14), cmd.shm_id); + EXPECT_EQ(static_cast<GLuint>(15), cmd.shm_offset); CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); }
diff --git a/gpu/command_buffer/gles2_cmd_buffer_functions.txt b/gpu/command_buffer/gles2_cmd_buffer_functions.txt index d23b34e..32e1907 100644 --- a/gpu/command_buffer/gles2_cmd_buffer_functions.txt +++ b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
@@ -328,7 +328,7 @@ GL_APICALL void GL_APIENTRY glDrawBuffersEXT (GLsizei count, const GLenum* bufs); GL_APICALL void GL_APIENTRY glDiscardBackbufferCHROMIUM (void); GL_APICALL void GL_APIENTRY glScheduleOverlayPlaneCHROMIUM (GLint plane_z_order, GLenum plane_transform, GLuint overlay_texture_id, GLint bounds_x, GLint bounds_y, GLint bounds_width, GLint bounds_height, GLfloat uv_x, GLfloat uv_y, GLfloat uv_width, GLfloat uv_height, GLboolean enable_blend, GLuint gpu_fence_id); -GL_APICALL void GL_APIENTRY glScheduleCALayerSharedStateCHROMIUM (GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, GLfloat clip_rect_corner_radius, GLint sorting_context_id, const GLfloat* transform); +GL_APICALL void GL_APIENTRY glScheduleCALayerSharedStateCHROMIUM (GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform); GL_APICALL void GL_APIENTRY glScheduleCALayerCHROMIUM (GLuint contents_texture_id, const GLfloat* contents_rect, GLuint background_color, GLuint edge_aa_mask, const GLfloat* bounds_rect, GLuint filter); GL_APICALL void GL_APIENTRY glScheduleCALayerInUseQueryCHROMIUM (GLsizei count, const GLuint* textures); GL_APICALL void GL_APIENTRY glCommitOverlayPlanesCHROMIUM (GLuint64 swap_id, GLbitfieldSwapBuffersFlags flags = 0);
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h index 9797118..9e76d76 100644 --- a/gpu/command_buffer/service/gl_utils.h +++ b/gpu/command_buffer/service/gl_utils.h
@@ -16,6 +16,7 @@ #include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/overlay_transform.h" +#include "ui/gfx/rrect_f.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_bindings.h" @@ -58,7 +59,7 @@ float opacity; bool is_clipped; gfx::Rect clip_rect; - float clip_rect_corner_radius; + gfx::RRectF rounded_corner_bounds; int sorting_context_id; gfx::Transform transform; };
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index b1b9b6cf..7756f497 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -13464,21 +13464,22 @@ const volatile gles2::cmds::ScheduleCALayerSharedStateCHROMIUM*>( cmd_data); + // 4 for |clip_rect|, 5 for |rounded_corner_bounds|, 16 for |transform|. const GLfloat* mem = GetSharedMemoryAs<const GLfloat*>(c.shm_id, c.shm_offset, - 20 * sizeof(GLfloat)); + 25 * sizeof(GLfloat)); if (!mem) { return error::kOutOfBounds; } gfx::RectF clip_rect(mem[0], mem[1], mem[2], mem[3]); - gfx::Transform transform(mem[4], mem[8], mem[12], mem[16], - mem[5], mem[9], mem[13], mem[17], - mem[6], mem[10], mem[14], mem[18], - mem[7], mem[11], mem[15], mem[19]); + gfx::RRectF rounded_corner_bounds(mem[4], mem[5], mem[6], mem[7], mem[8]); + gfx::Transform transform(mem[9], mem[13], mem[17], mem[21], mem[10], mem[14], + mem[18], mem[22], mem[11], mem[15], mem[19], mem[23], + mem[12], mem[16], mem[20], mem[24]); ca_layer_shared_state_.reset(new CALayerSharedState); ca_layer_shared_state_->opacity = c.opacity; ca_layer_shared_state_->is_clipped = c.is_clipped ? true : false; ca_layer_shared_state_->clip_rect = gfx::ToEnclosingRect(clip_rect); - ca_layer_shared_state_->clip_rect_corner_radius = c.clip_rect_corner_radius; + ca_layer_shared_state_->rounded_corner_bounds = rounded_corner_bounds; ca_layer_shared_state_->sorting_context_id = c.sorting_context_id; ca_layer_shared_state_->transform = transform; return error::kNoError; @@ -13533,7 +13534,7 @@ ui::CARendererLayerParams params = ui::CARendererLayerParams( ca_layer_shared_state_->is_clipped, ca_layer_shared_state_->clip_rect, - ca_layer_shared_state_->clip_rect_corner_radius, + ca_layer_shared_state_->rounded_corner_bounds, ca_layer_shared_state_->sorting_context_id, ca_layer_shared_state_->transform, image, contents_rect, gfx::ToEnclosingRect(bounds_rect), c.background_color, c.edge_aa_mask,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h index aba64996..c2eac26 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -900,7 +900,7 @@ GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform); error::Error DoScheduleCALayerCHROMIUM(GLuint contents_texture_id,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc index 295eec1..0e1006c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -4625,7 +4625,7 @@ GLfloat opacity, GLboolean is_clipped, const GLfloat* clip_rect, - GLfloat clip_rect_corner_radius, + const GLfloat* rounded_corner_bounds, GLint sorting_context_id, const GLfloat* transform) { if (!ca_layer_shared_state_) { @@ -4636,7 +4636,11 @@ ca_layer_shared_state_->is_clipped = is_clipped; ca_layer_shared_state_->clip_rect = gfx::ToEnclosingRect( gfx::RectF(clip_rect[0], clip_rect[1], clip_rect[2], clip_rect[3])); - ca_layer_shared_state_->clip_rect_corner_radius = clip_rect_corner_radius; + + ca_layer_shared_state_->rounded_corner_bounds = + gfx::RRectF(rounded_corner_bounds[0], rounded_corner_bounds[1], + rounded_corner_bounds[2], rounded_corner_bounds[3], + rounded_corner_bounds[4]); ca_layer_shared_state_->sorting_context_id = sorting_context_id; ca_layer_shared_state_->transform = gfx::Transform(transform[0], transform[4], transform[8], transform[12], @@ -4679,7 +4683,7 @@ ui::CARendererLayerParams params = ui::CARendererLayerParams( ca_layer_shared_state_->is_clipped, ca_layer_shared_state_->clip_rect, - ca_layer_shared_state_->clip_rect_corner_radius, + ca_layer_shared_state_->rounded_corner_bounds, ca_layer_shared_state_->sorting_context_id, ca_layer_shared_state_->transform, image, gfx::RectF(contents_rect[0], contents_rect[1], contents_rect[2],
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc index 55a9a57..50baa6cf 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
@@ -2060,21 +2060,21 @@ cmd_data); GLfloat opacity = static_cast<GLfloat>(c.opacity); GLboolean is_clipped = static_cast<GLboolean>(c.is_clipped); - GLfloat clip_rect_corner_radius = - static_cast<GLfloat>(c.clip_rect_corner_radius); GLint sorting_context_id = static_cast<GLint>(c.sorting_context_id); uint32_t shm_id = c.shm_id; uint32_t shm_offset = c.shm_offset; + // 4 for |clip_rect|, 5 for |rounded_corner_bounds|, 16 for |transform|. const GLfloat* mem = GetSharedMemoryAs<const GLfloat*>(shm_id, shm_offset, - 20 * sizeof(GLfloat)); + 25 * sizeof(GLfloat)); if (!mem) { return error::kOutOfBounds; } const GLfloat* clip_rect = mem + 0; - const GLfloat* transform = mem + 4; + const GLfloat* rounded_corner_bounds = mem + 4; + const GLfloat* transform = mem + 9; return DoScheduleCALayerSharedStateCHROMIUM(opacity, is_clipped, clip_rect, - clip_rect_corner_radius, + rounded_corner_bounds, sorting_context_id, transform); }
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm index ff60466..10b345d3 100644 --- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm +++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -259,10 +259,10 @@ return false; } const ui::CARendererLayerParams overlay_as_calayer_params( - false, // is_clipped - gfx::Rect(), // clip_rect - 0, // clip_rect_corner_radius - 0, // sorting_context_id + false, // is_clipped + gfx::Rect(), // clip_rect + gfx::RRectF(), // rounded_corner_bounds + 0, // sorting_context_id gfx::Transform(), image, crop_rect, // contents_rect pixel_frame_rect, // rect
diff --git a/headless/BUILD.gn b/headless/BUILD.gn index cbc5a29..8c202dd0 100644 --- a/headless/BUILD.gn +++ b/headless/BUILD.gn
@@ -357,8 +357,6 @@ "lib/browser/headless_quota_permission_context.h", "lib/browser/headless_request_context_manager.cc", "lib/browser/headless_request_context_manager.h", - "lib/browser/headless_resource_dispatcher_host_delegate.cc", - "lib/browser/headless_resource_dispatcher_host_delegate.h", "lib/browser/headless_shell_application_mac.h", "lib/browser/headless_shell_application_mac.mm", "lib/browser/headless_window_tree_host.h",
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc index 1677aaec..0f9ab4b 100644 --- a/headless/lib/browser/headless_content_browser_client.cc +++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -19,7 +19,6 @@ #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" @@ -298,13 +297,6 @@ return base::OnceClosure(); } -void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() { - resource_dispatcher_host_delegate_.reset( - new HeadlessResourceDispatcherHostDelegate); - content::ResourceDispatcherHost::Get()->SetDelegate( - resource_dispatcher_host_delegate_.get()); -} - bool HeadlessContentBrowserClient::ShouldEnableStrictSiteIsolation() { // TODO(lukasza): https://crbug.com/869494: Instead of overriding // ShouldEnableStrictSiteIsolation, //headless should inherit the default
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h index f87a1798..a45fdba 100644 --- a/headless/lib/browser/headless_content_browser_client.h +++ b/headless/lib/browser/headless_content_browser_client.h
@@ -8,7 +8,6 @@ #include <memory> #include "content/public/browser/content_browser_client.h" -#include "headless/lib/browser/headless_resource_dispatcher_host_delegate.h" #include "headless/public/headless_browser.h" namespace headless { @@ -60,7 +59,6 @@ net::SSLCertRequestInfo* cert_request_info, net::ClientCertIdentityList client_certs, std::unique_ptr<content::ClientCertificateDelegate> delegate) override; - void ResourceDispatcherHostCreated() override; bool ShouldEnableStrictSiteIsolation() override; ::network::mojom::NetworkContextPtr CreateNetworkContext( @@ -82,9 +80,6 @@ HeadlessBrowser::Options::AppendCommandLineFlagsCallback append_command_line_flags_callback_; - std::unique_ptr<HeadlessResourceDispatcherHostDelegate> - resource_dispatcher_host_delegate_; - DISALLOW_COPY_AND_ASSIGN(HeadlessContentBrowserClient); };
diff --git a/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc b/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc deleted file mode 100644 index d7b4888..0000000 --- a/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "headless/lib/browser/headless_resource_dispatcher_host_delegate.h" - -namespace headless { - -HeadlessResourceDispatcherHostDelegate:: - HeadlessResourceDispatcherHostDelegate() = default; - -HeadlessResourceDispatcherHostDelegate:: - ~HeadlessResourceDispatcherHostDelegate() = default; - -} // namespace headless
diff --git a/headless/lib/browser/headless_resource_dispatcher_host_delegate.h b/headless/lib/browser/headless_resource_dispatcher_host_delegate.h deleted file mode 100644 index 7600c9bf..0000000 --- a/headless/lib/browser/headless_resource_dispatcher_host_delegate.h +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef HEADLESS_LIB_BROWSER_HEADLESS_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ -#define HEADLESS_LIB_BROWSER_HEADLESS_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ - -#include "content/public/browser/resource_dispatcher_host_delegate.h" - -namespace headless { - -class HeadlessResourceDispatcherHostDelegate - : public content::ResourceDispatcherHostDelegate { - public: - HeadlessResourceDispatcherHostDelegate(); - ~HeadlessResourceDispatcherHostDelegate() override; - - private: - DISALLOW_COPY_AND_ASSIGN(HeadlessResourceDispatcherHostDelegate); -}; - -} // namespace headless - -#endif // HEADLESS_LIB_BROWSER_HEADLESS_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index 2271599..ba86d6f4 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -494,12 +494,6 @@ recipe { properties: "mastername:chromium.dawn" } - # Remove the cores dimensions for this mixin so that VMs of differing sizes - # can be used. - # This works here only because we use auto_builder_dimensions, - # so that only the bots that were earmarked for the builders - # will be allowed to use them anyways. - dimensions: "cores:" } builder_mixins { @@ -589,9 +583,19 @@ } builder_mixins { - name: "linux-dawn-ci" - mixins: "linux" + name: "linux-dawn-ci-builder" + dimensions: "cores:8" + mixins: "builderless" mixins: "dawn-ci" + mixins: "linux-xenial" +} + +builder_mixins { + name: "linux-dawn-ci-tester" + dimensions: "cores:2" + mixins: "builderless" + mixins: "dawn-ci" + mixins: "linux-xenial" } builder_mixins { @@ -881,8 +885,9 @@ builder_mixins { name: "win-dawn-ci" - mixins: "win" + mixins: "builderless" mixins: "dawn-ci" + mixins: "win" } builder_mixins { @@ -2275,6 +2280,9 @@ name: "GPU Win Builder (dbg)" mixins: "win-gpu-ci" } + # Note that the Win testers are all thin Linux VMs, triggering jobs on the + # physical Win hardware in the Swarming pool, and therefore use the + # linux-gpu-ci-tester mixin. builders { name: "Win10 Debug (NVIDIA)" mixins: "linux-gpu-ci-tester" @@ -2313,6 +2321,9 @@ name: "GPU FYI XR Win Builder" mixins: "win-gpu-fyi-ci" } + # Note that the Win testers are all thin Linux VMs, triggering jobs on the + # physical Win hardware in the Swarming pool, and therefore use the + # linux-gpu-fyi-ci-tester mixin. builders { name: "Win10 FYI Debug (NVIDIA)" mixins: "linux-gpu-fyi-ci-tester" @@ -2382,29 +2393,29 @@ # chromium.dawn builders { name: "Dawn Linux x64 Builder" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-builder" mixins: "goma-rbe-prod" } builders { name: "Dawn Linux x64 Release (Intel HD 630)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Linux x64 Release (NVIDIA)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Linux x64 DEPS Builder" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-builder" mixins: "goma-rbe-prod" } builders { name: "Dawn Linux x64 DEPS Release (Intel HD 630)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Linux x64 DEPS Release (NVIDIA)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Mac x64 Builder" @@ -2418,22 +2429,22 @@ } # Note that the Mac testers are all thin Linux VMs, triggering jobs on the # physical Mac hardware in the Swarming pool, and therefore use the - # linux-dawn-ci mixin. + # linux-dawn-ci-tester mixin. builders { name: "Dawn Mac x64 Release (AMD)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Mac x64 Release (Intel)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Mac x64 DEPS Release (AMD)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Mac x64 DEPS Release (Intel)" - mixins: "linux-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x86 Builder" @@ -2443,21 +2454,24 @@ name: "Dawn Win10 x64 Builder" mixins: "win-dawn-ci" } + # Note that the Win testers are all thin Linux VMs, triggering jobs on the + # physical Win hardware in the Swarming pool, and therefore use the + # linux-dawn-ci-tester mixin. builders { name: "Dawn Win10 x86 Release (Intel HD 630)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x64 Release (Intel HD 630)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x86 Release (NVIDIA)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x64 Release (NVIDIA)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x86 DEPS Builder" @@ -2469,19 +2483,19 @@ } builders { name: "Dawn Win10 x86 DEPS Release (Intel HD 630)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x64 DEPS Release (Intel HD 630)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x86 DEPS Release (NVIDIA)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } builders { name: "Dawn Win10 x64 DEPS Release (NVIDIA)" - mixins: "win-dawn-ci" + mixins: "linux-dawn-ci-tester" } @@ -3541,7 +3555,7 @@ builders { name: "findit_variable" } builders { name: "linux_chromium_bot_db_exporter" - mixins: "linux" + mixins: "linux-xenial" recipe { name: "findit/chromium/export_bot_db" properties: "gs_bucket:findit-for-me"
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn index 571d16a5..67f8e5a 100644 --- a/ios/web_view/BUILD.gn +++ b/ios/web_view/BUILD.gn
@@ -413,6 +413,7 @@ "internal/cwv_preview_element_info_unittest.mm", "internal/cwv_scroll_view_unittest.mm", "internal/cwv_ssl_status_unittest.mm", + "internal/cwv_web_view_configuration_unittest.mm", "internal/signin/cwv_identity_unittest.mm", "internal/sync/cwv_sync_controller_unittest.mm", "internal/translate/cwv_translation_controller_unittest.mm",
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm index bb60bb5..1154e1c 100644 --- a/ios/web_view/internal/cwv_web_view_configuration.mm +++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -35,9 +35,6 @@ // Holds all CWVWebViews created with this class. Weak references. NSHashTable* _webViews; - - // |YES| if |shutDown| was called. - BOOL _wasShutDown; } #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) @@ -131,10 +128,6 @@ return self; } -- (void)dealloc { - DCHECK(_wasShutDown); -} - #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL) #pragma mark - Autofill - (CWVAutofillDataManager*)autofillDataManager { @@ -200,7 +193,6 @@ [webView shutDown]; } _browserState.reset(); - _wasShutDown = YES; } @end
diff --git a/ios/web_view/internal/cwv_web_view_configuration_internal.h b/ios/web_view/internal/cwv_web_view_configuration_internal.h index 572d5ef..e2c227ddb 100644 --- a/ios/web_view/internal/cwv_web_view_configuration_internal.h +++ b/ios/web_view/internal/cwv_web_view_configuration_internal.h
@@ -7,6 +7,8 @@ #import "ios/web_view/public/cwv_web_view_configuration.h" +#include <memory> + NS_ASSUME_NONNULL_BEGIN namespace ios_web_view { @@ -17,19 +19,25 @@ @interface CWVWebViewConfiguration () +// The browser state associated with this configuration. +@property(nonatomic, readonly) ios_web_view::WebViewBrowserState* browserState; + // Calls |shutDown| on the singletons returned by |defaultConfiguration| and // |incognitoConfiguration|. + (void)shutDown; -// The browser state associated with this configuration. -@property(nonatomic, readonly) ios_web_view::WebViewBrowserState* browserState; +// Initializes with |browserState| that this instance is to be associated with. +- (instancetype)initWithBrowserState: + (std::unique_ptr<ios_web_view::WebViewBrowserState>)browserState + NS_DESIGNATED_INITIALIZER; // Registers a |webView| so that this class can call |shutDown| on it later on. // Only weak references are held, so no need for de-register method. - (void)registerWebView:(CWVWebView*)webView; -// Because Obj-C classes under ARC tend to outlive C++ classes, this method is -// needed to cleanly tear down this object. Must be called before |dealloc|. +// If this instance is going to outlive the globals created in +// ios_web_view::InitializeGlobalState, this method must be called to ensure the +// correct tear down order. - (void)shutDown; @end
diff --git a/ios/web_view/internal/cwv_web_view_configuration_unittest.mm b/ios/web_view/internal/cwv_web_view_configuration_unittest.mm new file mode 100644 index 0000000..c7b9203 --- /dev/null +++ b/ios/web_view/internal/cwv_web_view_configuration_unittest.mm
@@ -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. + +#import "ios/web_view/internal/cwv_web_view_configuration_internal.h" + +#include <memory> + +#include "ios/web/public/test/test_web_thread_bundle.h" +#include "ios/web_view/internal/web_view_browser_state.h" +#include "ios/web_view/test/test_with_locale_and_resources.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace ios_web_view { + +class CWVWebViewConfigurationTest : public TestWithLocaleAndResources { + protected: + web::TestWebThreadBundle web_thread_bundle_; +}; + +// Test CWVWebViewConfiguration initialization. +TEST_F(CWVWebViewConfigurationTest, Initialization) { + std::unique_ptr<WebViewBrowserState> browser_state = + std::make_unique<WebViewBrowserState>(/*off_the_record=*/false); + WebViewBrowserState* browser_state_ptr = browser_state.get(); + CWVWebViewConfiguration* configuration = [[CWVWebViewConfiguration alloc] + initWithBrowserState:std::move(browser_state)]; + EXPECT_EQ(browser_state_ptr, configuration.browserState); + EXPECT_TRUE(configuration.persistent); +} + +// Test CWVWebViewConfiguration properly shuts down. +TEST_F(CWVWebViewConfigurationTest, ShutDown) { + std::unique_ptr<WebViewBrowserState> browser_state = + std::make_unique<WebViewBrowserState>(/*off_the_record=*/false); + CWVWebViewConfiguration* configuration = [[CWVWebViewConfiguration alloc] + initWithBrowserState:std::move(browser_state)]; + EXPECT_TRUE(configuration.browserState); + [configuration shutDown]; + EXPECT_FALSE(configuration.browserState); +} + +} // namespace ios_web_view
diff --git a/ios/web_view/internal/web_view_global_state_util.mm b/ios/web_view/internal/web_view_global_state_util.mm index a330706..33381a7 100644 --- a/ios/web_view/internal/web_view_global_state_util.mm +++ b/ios/web_view/internal/web_view_global_state_util.mm
@@ -18,6 +18,13 @@ namespace ios_web_view { void InitializeGlobalState() { +#if defined(UNIT_TEST) + // Do not perform global state initialization in an unit test environment. + // 1. Not needed when unit testing. + // 2. The globals below will try to create other already created globals like + // AtExitManagers. This causes DCHECKs and prevents tests from completing. + return; +#endif // defined(UNIT_TEST) static std::unique_ptr<ios_web_view::WebViewWebClient> web_client; static std::unique_ptr<ios_web_view::WebViewWebMainDelegate> web_main_delegate;
diff --git a/media/base/media_resource.cc b/media/base/media_resource.cc index 1a058d9..70142e1 100644 --- a/media/base/media_resource.cc +++ b/media/base/media_resource.cc
@@ -12,7 +12,7 @@ MediaUrlParams MediaResource::GetMediaUrlParams() const { NOTREACHED(); - return MediaUrlParams{GURL(), GURL()}; + return MediaUrlParams{GURL(), GURL(), false, false}; } MediaResource::Type MediaResource::GetType() const {
diff --git a/media/base/media_url_demuxer.cc b/media/base/media_url_demuxer.cc index a818001..c2f645b5 100644 --- a/media/base/media_url_demuxer.cc +++ b/media/base/media_url_demuxer.cc
@@ -13,8 +13,9 @@ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const GURL& media_url, const GURL& site_for_cookies, - bool allow_credentials) - : params_{media_url, site_for_cookies, allow_credentials}, + bool allow_credentials, + bool is_hls) + : params_{media_url, site_for_cookies, allow_credentials, is_hls}, task_runner_(task_runner) {} MediaUrlDemuxer::~MediaUrlDemuxer() = default;
diff --git a/media/base/media_url_demuxer.h b/media/base/media_url_demuxer.h index 7c5f15b..9e8a044 100644 --- a/media/base/media_url_demuxer.h +++ b/media/base/media_url_demuxer.h
@@ -36,7 +36,8 @@ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const GURL& media_url, const GURL& site_for_cookies, - bool allow_credentials); + bool allow_credentials, + bool is_hls); ~MediaUrlDemuxer() override; // MediaResource interface.
diff --git a/media/base/media_url_demuxer_unittest.cc b/media/base/media_url_demuxer_unittest.cc index 64b9e775..56a8bb0 100644 --- a/media/base/media_url_demuxer_unittest.cc +++ b/media/base/media_url_demuxer_unittest.cc
@@ -26,7 +26,7 @@ bool allow_credentials) { demuxer_.reset(new MediaUrlDemuxer(base::ThreadTaskRunnerHandle::Get(), media_url, first_party, - allow_credentials)); + allow_credentials, false)); } void InitializeTest() {
diff --git a/media/base/media_url_params.h b/media/base/media_url_params.h index 630fcf5..c59e1ef 100644 --- a/media/base/media_url_params.h +++ b/media/base/media_url_params.h
@@ -5,6 +5,7 @@ #ifndef MEDIA_BASE_MEDIA_URL_PARAMS_H_ #define MEDIA_BASE_MEDIA_URL_PARAMS_H_ +#include "media/base/media_export.h" #include "url/gurl.h" namespace media { @@ -33,6 +34,10 @@ // initiated in MediaResourceGetter::CheckPolicyForCookies() for the given // |site_for_cookies|. bool allow_credentials; + + // True when MediaPlayerRenderer has been selected because the media has been + // detected to be HLS. Used only for metrics. + bool is_hls; }; } // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index ebf2436..c1a2f1aa3 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -2627,7 +2627,7 @@ demuxer_.reset(new MediaUrlDemuxer( media_task_runner_, loaded_url_, frame_->GetDocument().SiteForCookies(), - allow_media_player_renderer_credentials_)); + allow_media_player_renderer_credentials_, demuxer_found_hls_)); pipeline_controller_->Start(Pipeline::StartType::kNormal, demuxer_.get(), this, false, false); return;
diff --git a/media/mojo/clients/mojo_renderer.cc b/media/mojo/clients/mojo_renderer.cc index be6d826..f37c711 100644 --- a/media/mojo/clients/mojo_renderer.cc +++ b/media/mojo/clients/mojo_renderer.cc
@@ -102,8 +102,7 @@ // |remote_renderer_|, and the callback won't be dispatched if // |remote_renderer_| is destroyed. remote_renderer_->Initialize( - std::move(client_ptr_info), std::move(stream_proxies), base::nullopt, - base::nullopt, /* allow_credentials */ false, + std::move(client_ptr_info), std::move(stream_proxies), nullptr, base::Bind(&MojoRenderer::OnInitialized, base::Unretained(this), client)); } @@ -121,10 +120,11 @@ // Using base::Unretained(this) is safe because |this| owns // |remote_renderer_|, and the callback won't be dispatched if // |remote_renderer_| is destroyed. - std::vector<mojom::DemuxerStreamPtrInfo> streams; + mojom::MediaUrlParamsPtr media_url_params = mojom::MediaUrlParams::New( + url_params.media_url, url_params.site_for_cookies, + url_params.allow_credentials, url_params.is_hls); remote_renderer_->Initialize( - std::move(client_ptr_info), std::move(streams), url_params.media_url, - url_params.site_for_cookies, url_params.allow_credentials, + std::move(client_ptr_info), base::nullopt, std::move(media_url_params), base::Bind(&MojoRenderer::OnInitialized, base::Unretained(this), client)); }
diff --git a/media/mojo/interfaces/renderer.mojom b/media/mojo/interfaces/renderer.mojom index ccb0c51..ba47e75 100644 --- a/media/mojo/interfaces/renderer.mojom +++ b/media/mojo/interfaces/renderer.mojom
@@ -11,15 +11,22 @@ import "ui/gfx/geometry/mojo/geometry.mojom"; import "url/mojom/url.mojom"; +// See media/base/media_url_params.h for descriptions. +struct MediaUrlParams { + url.mojom.Url media_url; + url.mojom.Url site_for_cookies; + bool allow_credentials; + bool is_hls; +}; + interface Renderer { - // Initializes the Renderer with either one or more audio / video |streams|, - // or a |media_url| executing the callback with whether the initialization - // succeeded. + // Initializes the Renderer with one or more audio / video |streams|, or a URL + // via |media_url_params|. Exactly one of |streams| and |media_urls_params| + // should be set. + // TODO(sandersd): These should be separate methods or a union type. Initialize(associated RendererClient client, array<DemuxerStream>? streams, - url.mojom.Url? media_url, - url.mojom.Url? first_party_for_cookies, bool allow_credentials) - => (bool success); + MediaUrlParams? media_url_params) => (bool success); // Discards any buffered data, executing callback when completed. // NOTE: If an error occurs, RendererClient::OnError() can be called
diff --git a/media/mojo/services/media_service_unittest.cc b/media/mojo/services/media_service_unittest.cc index 2752b1a..b63763d 100644 --- a/media/mojo/services/media_service_unittest.cc +++ b/media/mojo/services/media_service_unittest.cc
@@ -266,8 +266,7 @@ EXPECT_CALL(*this, OnRendererInitialized(expected_result)) .WillOnce(QuitLoop(&run_loop)); renderer_->Initialize( - std::move(client_ptr_info), std::move(streams), base::nullopt, - base::nullopt, /* allow_credentials */ false, + std::move(client_ptr_info), std::move(streams), nullptr, base::BindOnce(&MediaServiceTest::OnRendererInitialized, base::Unretained(this))); run_loop.Run();
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc index e175a0e6..f0730fc 100644 --- a/media/mojo/services/mojo_renderer_service.cc +++ b/media/mojo/services/mojo_renderer_service.cc
@@ -66,9 +66,7 @@ void MojoRendererService::Initialize( mojom::RendererClientAssociatedPtrInfo client, base::Optional<std::vector<mojom::DemuxerStreamPtrInfo>> streams, - const base::Optional<GURL>& media_url, - const base::Optional<GURL>& site_for_cookies, - bool allow_credentials, + mojom::MediaUrlParamsPtr media_url_params, InitializeCallback callback) { DVLOG(1) << __func__; DCHECK_EQ(state_, STATE_UNINITIALIZED); @@ -76,7 +74,7 @@ client_.Bind(std::move(client)); state_ = STATE_INITIALIZING; - if (media_url == base::nullopt) { + if (!media_url_params) { DCHECK(streams.has_value()); media_resource_.reset(new MediaResourceShim( std::move(*streams), base::Bind(&MojoRendererService::OnStreamReady, @@ -84,10 +82,11 @@ return; } - DCHECK(!media_url.value().is_empty()); - DCHECK(site_for_cookies); + DCHECK(!media_url_params->media_url.is_empty()); + DCHECK(!media_url_params->site_for_cookies.is_empty()); media_resource_.reset(new MediaUrlDemuxer( - nullptr, media_url.value(), site_for_cookies.value(), allow_credentials)); + nullptr, media_url_params->media_url, media_url_params->site_for_cookies, + media_url_params->allow_credentials, media_url_params->is_hls)); renderer_->Initialize( media_resource_.get(), this, base::Bind(&MojoRendererService::OnRendererInitializeDone, weak_this_,
diff --git a/media/mojo/services/mojo_renderer_service.h b/media/mojo/services/mojo_renderer_service.h index aa1621a..e90125a 100644 --- a/media/mojo/services/mojo_renderer_service.h +++ b/media/mojo/services/mojo_renderer_service.h
@@ -53,9 +53,7 @@ void Initialize( mojom::RendererClientAssociatedPtrInfo client, base::Optional<std::vector<mojom::DemuxerStreamPtrInfo>> streams, - const base::Optional<GURL>& media_url, - const base::Optional<GURL>& site_for_cookies, - bool allow_credentials, + mojom::MediaUrlParamsPtr media_url_params, InitializeCallback callback) final; void Flush(FlushCallback callback) final; void StartPlayingFrom(base::TimeDelta time_delta) final;
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc index 6603eb8..2f849ac 100644 --- a/net/network_error_logging/network_error_logging_service.cc +++ b/net/network_error_logging/network_error_logging_service.cc
@@ -22,6 +22,7 @@ #include "base/values.h" #include "net/base/ip_address.h" #include "net/base/net_errors.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/log/net_log.h" #include "net/reporting/reporting_service.h" #include "url/gurl.h" @@ -383,11 +384,20 @@ policy.received_ip_address = received_ip_address; policy.last_used = header_received_time; HeaderOutcome outcome = ParseHeader(value, clock_->Now(), &policy); + // Disallow eTLDs from setting include_subdomains policies. + if ((outcome == HeaderOutcome::SET || outcome == HeaderOutcome::REMOVED) && + policy.include_subdomains && + registry_controlled_domains::GetRegistryLength( + policy.origin.GetURL(), + registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES, + registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) == 0) { + outcome = HeaderOutcome::DISCARDED_INCLUDE_SUBDOMAINS_NOT_ALLOWED; + } RecordHeaderOutcome(outcome); if (outcome != HeaderOutcome::SET && outcome != HeaderOutcome::REMOVED) return; - // If a policy for |origin| already existed, remove the old poliicy. + // If a policy for |origin| already existed, remove the old policy. auto it = policies_.find(origin); if (it != policies_.end()) RemovePolicy(it);
diff --git a/net/network_error_logging/network_error_logging_service.h b/net/network_error_logging/network_error_logging_service.h index 7f38335..df0c406 100644 --- a/net/network_error_logging/network_error_logging_service.h +++ b/net/network_error_logging/network_error_logging_service.h
@@ -169,6 +169,7 @@ SET = 13, DISCARDED_MISSING_REMOTE_ENDPOINT = 14, + DISCARDED_INCLUDE_SUBDOMAINS_NOT_ALLOWED = 15, MAX };
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc index c1203fc..ba7e5fe 100644 --- a/net/network_error_logging/network_error_logging_service_unittest.cc +++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -140,6 +140,7 @@ const GURL kUrlDifferentPort_ = GURL("https://example.com:4433/path"); const GURL kUrlSubdomain_ = GURL("https://subdomain.example.com/path"); const GURL kUrlDifferentHost_ = GURL("https://example2.com/path"); + const GURL kUrlEtld_ = GURL("https://co.uk/foo.html"); const GURL kInnerUrl_ = GURL("https://example.net/path"); const GURL kCertUrl_ = GURL("https://example.com/cert_path"); @@ -152,6 +153,7 @@ const url::Origin kOriginSubdomain_ = url::Origin::Create(kUrlSubdomain_); const url::Origin kOriginDifferentHost_ = url::Origin::Create(kUrlDifferentHost_); + const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_); const std::string kHeader_ = "{\"report_to\":\"group\",\"max_age\":86400}"; const std::string kHeaderSuccessFraction0_ = @@ -237,6 +239,33 @@ EXPECT_TRUE(reports().empty()); } +TEST_P(NetworkErrorLoggingServiceTest, IncludeSubdomainsEtldRejected) { + service()->OnHeader(kOriginEtld_, kServerIP_, kHeaderIncludeSubdomains_); + + // Make the rest of the test run synchronously. + FinishLoading(true /* load_success */); + + EXPECT_EQ(0u, PolicyCount()); + + service()->OnRequest(MakeRequestDetails(kUrlEtld_, ERR_CONNECTION_REFUSED)); + + EXPECT_TRUE(reports().empty()); +} + +TEST_P(NetworkErrorLoggingServiceTest, NonIncludeSubdomainsEtldAccepted) { + service()->OnHeader(kOriginEtld_, kServerIP_, kHeader_); + + // Make the rest of the test run synchronously. + FinishLoading(true /* load_success */); + + EXPECT_EQ(1u, PolicyCount()); + + service()->OnRequest(MakeRequestDetails(kUrlEtld_, ERR_CONNECTION_REFUSED)); + + EXPECT_EQ(1u, reports().size()); + EXPECT_EQ(kUrlEtld_, reports()[0].url); +} + TEST_P(NetworkErrorLoggingServiceTest, SuccessReportQueued) { service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1_);
diff --git a/net/nqe/network_congestion_analyzer.cc b/net/nqe/network_congestion_analyzer.cc index 1087d1d..87aea025 100644 --- a/net/nqe/network_congestion_analyzer.cc +++ b/net/nqe/network_congestion_analyzer.cc
@@ -290,6 +290,19 @@ ComputePeakQueueingDelayLevel(peak_queueing_delay_); DCHECK_GE(kQueueingDelayLevelMaxVal, peak_queueing_delay_level); + if (peak_queueing_delay_level >= kQueueingDelayLevelMinVal && + peak_queueing_delay_level <= kQueueingDelayLevelMaxVal && + peak_count_inflight_requests_measurement_period_ >= 3) { + // Records the count of in-flight requests causing the peak queueing delay + // within the current measurement period. These samples are bucketized + // into 10 peak queueing delay levels. + base::UmaHistogramCounts100( + "NQE.CongestionAnalyzer.CountInflightRequestsForPeakQueueingDelay." + "Level" + + base::NumberToString(peak_queueing_delay_level), + count_inflight_requests_for_peak_queueing_delay_); + } + // Resets the tracked data for the new measurement period. peak_queueing_delay_ = delay; count_inflight_requests_for_peak_queueing_delay_ = count_inflight_requests;
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc index 222a92e7..19a407a 100644 --- a/net/reporting/reporting_header_parser.cc +++ b/net/reporting/reporting_header_parser.cc
@@ -14,6 +14,7 @@ #include "base/metrics/histogram_macros.h" #include "base/time/time.h" #include "base/values.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/reporting/reporting_cache.h" #include "net/reporting/reporting_context.h" #include "net/reporting/reporting_delegate.h" @@ -143,6 +144,15 @@ if (dict->HasKey(kIncludeSubdomainsKey) && dict->GetBoolean(kIncludeSubdomainsKey, &subdomains_bool) && subdomains_bool == true) { + // Disallow eTLDs from setting include_subdomains endpoint groups. + if (registry_controlled_domains::GetRegistryLength( + origin.GetURL(), + registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES, + registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) == 0) { + return HeaderEndpointGroupOutcome:: + DISCARDED_INCLUDE_SUBDOMAINS_NOT_ALLOWED; + } + parsed_endpoint_group_out->include_subdomains = OriginSubdomains::INCLUDE; }
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h index 8de7d2f..58884265 100644 --- a/net/reporting/reporting_header_parser.h +++ b/net/reporting/reporting_header_parser.h
@@ -51,6 +51,7 @@ PARSED = 7, REMOVED_TTL_ZERO = 8, REMOVED_EMPTY = 9, + DISCARDED_INCLUDE_SUBDOMAINS_NOT_ALLOWED = 10, MAX };
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc index 53cb6b9..1ff3f73 100644 --- a/net/reporting/reporting_header_parser_unittest.cc +++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -139,6 +139,8 @@ const GURL kUrl2_ = GURL("https://origin2.test/path"); const url::Origin kOrigin2_ = url::Origin::Create(GURL("https://origin2.test/")); + const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/"); + const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_); const GURL kEndpoint_ = GURL("https://endpoint.test/"); const GURL kEndpoint2_ = GURL("https://endpoint2.test/"); const GURL kEndpoint3_ = GURL("https://endpoint3.test/"); @@ -360,6 +362,34 @@ } } +TEST_P(ReportingHeaderParserTest, IncludeSubdomainsEtldRejected) { + std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint_}}; + + std::string header = ConstructHeaderGroupString( + MakeEndpointGroup(kGroup_, endpoints, OriginSubdomains::INCLUDE)); + ParseHeader(kUrlEtld_, header); + + EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting()); + EXPECT_FALSE(EndpointGroupExistsInCache(kOriginEtld_, kGroup_, + OriginSubdomains::INCLUDE)); + EXPECT_EQ(0u, cache()->GetEndpointCount()); + EXPECT_FALSE(EndpointExistsInCache(kOriginEtld_, kGroup_, kEndpoint_)); +} + +TEST_P(ReportingHeaderParserTest, NonIncludeSubdomainsEtldAccepted) { + std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint_}}; + + std::string header = ConstructHeaderGroupString( + MakeEndpointGroup(kGroup_, endpoints, OriginSubdomains::EXCLUDE)); + ParseHeader(kUrlEtld_, header); + + EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); + EXPECT_TRUE(EndpointGroupExistsInCache(kOriginEtld_, kGroup_, + OriginSubdomains::EXCLUDE)); + EXPECT_EQ(1u, cache()->GetEndpointCount()); + EXPECT_TRUE(EndpointExistsInCache(kOriginEtld_, kGroup_, kEndpoint_)); +} + TEST_P(ReportingHeaderParserTest, IncludeSubdomainsNotBoolean) { std::string header = "{\"group\": \"" + kGroup_ +
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h index 341e291..a786ddd 100644 --- a/sandbox/win/src/nt_internals.h +++ b/sandbox/win/src/nt_internals.h
@@ -13,11 +13,14 @@ typedef LONG NTSTATUS; #define NT_SUCCESS(st) (st >= 0) +#define NT_ERROR(st) ((((ULONG)(st)) >> 30) == 3) +// clang-format off #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) #define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) +#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003L) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #ifndef STATUS_INVALID_PARAMETER // It is now defined in Windows 2008 SDK. @@ -32,6 +35,7 @@ #define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL) #define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL) #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) +// clang-format on #define CURRENT_PROCESS ((HANDLE)-1) #define CURRENT_THREAD ((HANDLE)-2) @@ -311,7 +315,17 @@ ProcessExecuteFlags = 0x22 } PROCESSINFOCLASS; -// Partial definition only. +// For the structure documentation, see +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx +typedef struct _RTL_USER_PROCESS_PARAMETERS { + BYTE Reserved1[16]; + PVOID Reserved2[10]; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +// Partial definition only, from +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx typedef struct _PEB { BYTE InheritedAddressSpace; BYTE ReadImageFileExecOptions; @@ -319,6 +333,8 @@ BYTE SpareBool; PVOID Mutant; PVOID ImageBaseAddress; + PVOID Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; } PEB, *PPEB; typedef LONG KPRIORITY;
diff --git a/services/file/BUILD.gn b/services/file/BUILD.gn index 3e562ca..eb9c0e7 100644 --- a/services/file/BUILD.gn +++ b/services/file/BUILD.gn
@@ -19,7 +19,7 @@ "//components/services/filesystem:lib", "//components/services/filesystem/public/mojom", "//components/services/leveldb:lib", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//mojo/public/cpp/bindings", "//services/file/public/mojom", "//services/service_manager/public/cpp",
diff --git a/services/file/file_service.h b/services/file/file_service.h index 96d86f0..88b2f4d 100644 --- a/services/file/file_service.h +++ b/services/file/file_service.h
@@ -9,7 +9,7 @@ #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" #include "components/services/filesystem/lock_table.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "services/file/public/mojom/file_system.mojom.h"
diff --git a/services/file/public/cpp/BUILD.gn b/services/file/public/cpp/BUILD.gn index 65fcbf1..8112a908 100644 --- a/services/file/public/cpp/BUILD.gn +++ b/services/file/public/cpp/BUILD.gn
@@ -10,7 +10,7 @@ deps = [ "//base", - "//components/services/leveldb/public/interfaces", + "//components/services/leveldb/public/mojom", "//services/file/public/mojom", "//services/service_manager/public/cpp", ]
diff --git a/services/file/public/cpp/manifest.cc b/services/file/public/cpp/manifest.cc index 6577002a..4d2f845 100644 --- a/services/file/public/cpp/manifest.cc +++ b/services/file/public/cpp/manifest.cc
@@ -5,7 +5,7 @@ #include "services/file/public/cpp/manifest.h" #include "base/no_destructor.h" -#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/services/leveldb/public/mojom/leveldb.mojom.h" #include "services/file/public/mojom/constants.mojom.h" #include "services/file/public/mojom/file_system.mojom.h" #include "services/service_manager/public/cpp/manifest_builder.h"
diff --git a/services/metrics/public/cpp/delegating_ukm_recorder.cc b/services/metrics/public/cpp/delegating_ukm_recorder.cc index 1ae9063..4a7b5fd 100644 --- a/services/metrics/public/cpp/delegating_ukm_recorder.cc +++ b/services/metrics/public/cpp/delegating_ukm_recorder.cc
@@ -87,6 +87,12 @@ iterator.second.AddEntry(entry->Clone()); } +void DelegatingUkmRecorder::MarkSourceForDeletion(ukm::SourceId source_id) { + base::AutoLock auto_lock(lock_); + for (auto& iterator : delegates_) + iterator.second.MarkSourceForDeletion(source_id); +} + DelegatingUkmRecorder::Delegate::Delegate( scoped_refptr<base::SequencedTaskRunner> task_runner, base::WeakPtr<UkmRecorder> ptr) @@ -139,4 +145,15 @@ std::move(entry))); } +void DelegatingUkmRecorder::Delegate::MarkSourceForDeletion( + ukm::SourceId source_id) { + if (task_runner_->RunsTasksInCurrentSequence()) { + ptr_->MarkSourceForDeletion(source_id); + return; + } + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&UkmRecorder::MarkSourceForDeletion, ptr_, source_id)); +} + } // namespace ukm
diff --git a/services/metrics/public/cpp/delegating_ukm_recorder.h b/services/metrics/public/cpp/delegating_ukm_recorder.h index 7bec6e8..54ae229 100644 --- a/services/metrics/public/cpp/delegating_ukm_recorder.h +++ b/services/metrics/public/cpp/delegating_ukm_recorder.h
@@ -54,6 +54,7 @@ SourceId source_id, const UkmSource::NavigationData& navigation_data) override; void AddEntry(mojom::UkmEntryPtr entry) override; + void MarkSourceForDeletion(SourceId source_id) override; class Delegate final { public: @@ -67,6 +68,7 @@ void RecordNavigation(SourceId source_id, const UkmSource::NavigationData& navigation_data); void AddEntry(mojom::UkmEntryPtr entry); + void MarkSourceForDeletion(SourceId source_id); private: scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/services/metrics/public/cpp/mojo_ukm_recorder.cc b/services/metrics/public/cpp/mojo_ukm_recorder.cc index 164729e..6b70d5c 100644 --- a/services/metrics/public/cpp/mojo_ukm_recorder.cc +++ b/services/metrics/public/cpp/mojo_ukm_recorder.cc
@@ -47,4 +47,8 @@ interface_->AddEntry(std::move(entry)); } +void MojoUkmRecorder::MarkSourceForDeletion(ukm::SourceId source_id) { + NOTREACHED(); +} + } // namespace ukm
diff --git a/services/metrics/public/cpp/mojo_ukm_recorder.h b/services/metrics/public/cpp/mojo_ukm_recorder.h index 5b34e84..2b01308 100644 --- a/services/metrics/public/cpp/mojo_ukm_recorder.h +++ b/services/metrics/public/cpp/mojo_ukm_recorder.h
@@ -49,6 +49,7 @@ SourceId source_id, const UkmSource::NavigationData& navigation_data) override; void AddEntry(mojom::UkmEntryPtr entry) override; + void MarkSourceForDeletion(ukm::SourceId source_id) override; mojom::UkmRecorderInterfacePtr interface_;
diff --git a/services/metrics/public/cpp/ukm_recorder.h b/services/metrics/public/cpp/ukm_recorder.h index 6d45c04..b4cb17e 100644 --- a/services/metrics/public/cpp/ukm_recorder.h +++ b/services/metrics/public/cpp/ukm_recorder.h
@@ -92,6 +92,11 @@ SourceId source_id, const UkmSource::NavigationData& navigation_data) = 0; + // Marks a source as no longer needed to kept alive in memory. Called by + // SourceUrlRecorderWebContentsObserver when a browser tab or its WebContents + // are no longer alive. Not to be used through mojo interface. + virtual void MarkSourceForDeletion(ukm::SourceId source_id) = 0; + DISALLOW_COPY_AND_ASSIGN(UkmRecorder); };
diff --git a/services/network/resource_scheduler/resource_scheduler.cc b/services/network/resource_scheduler/resource_scheduler.cc index 306ae4b..86291174 100644 --- a/services/network/resource_scheduler/resource_scheduler.cc +++ b/services/network/resource_scheduler/resource_scheduler.cc
@@ -1031,9 +1031,56 @@ } } + if (IsNonDelayableRequestAnticipated()) { + return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; + } + return START_REQUEST; } + // Returns true if a non-delayable request is expected to arrive soon. + bool IsNonDelayableRequestAnticipated() const { + base::Optional<double> http_rtt_multiplier = + params_for_network_quality_ + .http_rtt_multiplier_for_proactive_throttling; + + if (!http_rtt_multiplier.has_value()) + return false; + + if (http_rtt_multiplier <= 0) + return false; + + // Currently, the heuristic for predicting the arrival of a non-delayable + // request makes a prediction only if a non-delayable request has started + // previously in this resource scheduler client. + if (!last_non_delayable_request_start_.has_value()) + return false; + + base::Optional<base::TimeDelta> http_rtt = + network_quality_estimator_->GetHttpRTT(); + if (!http_rtt.has_value()) + return false; + + base::TimeDelta threshold_for_proactive_throttling = + http_rtt.value() * http_rtt_multiplier.value(); + base::TimeDelta time_since_last_non_delayable_request_start = + tick_clock_->NowTicks() - last_non_delayable_request_start_.value(); + + if (time_since_last_non_delayable_request_start >= + threshold_for_proactive_throttling) { + // Last non-delayable request started more than + // |threshold_for_proactive_throttling| back. The algorithm estimates that + // by this time any non-delayable that were triggered by requests that + // started long time back would have arrived at resource scheduler by now. + // + // On the other hand, if the last non-delayable request started recently, + // then it's likely that the parsing of the response from that recently + // started request would trigger additional non-delayable requests. + return false; + } + return true; + } + // It is common for a burst of messages to come from the renderer which // trigger starting pending requests. Naively, this would result in O(n*m) // behavior for n pending requests and m <= n messages, as
diff --git a/services/network/resource_scheduler/resource_scheduler_unittest.cc b/services/network/resource_scheduler/resource_scheduler_unittest.cc index c15a719..20c33bc4 100644 --- a/services/network/resource_scheduler/resource_scheduler_unittest.cc +++ b/services/network/resource_scheduler/resource_scheduler_unittest.cc
@@ -472,6 +472,29 @@ EXPECT_FALSE(last_low->started()); } + void ConfigureProactiveThrottlingExperimentFor2G( + double http_rtt_multiplier_for_proactive_throttling) { + std::map<net::EffectiveConnectionType, + ResourceSchedulerParamsManager::ParamsForNetworkQuality> + params_for_network_quality_container; + ResourceSchedulerParamsManager::ParamsForNetworkQuality params_2g; + + params_2g.http_rtt_multiplier_for_proactive_throttling = + http_rtt_multiplier_for_proactive_throttling; + + params_for_network_quality_container[net::EFFECTIVE_CONNECTION_TYPE_2G] = + params_2g; + + resource_scheduler_params_manager_.Reset( + params_for_network_quality_container); + + network_quality_estimator_.SetAndNotifyObserversOfEffectiveConnectionType( + net::EFFECTIVE_CONNECTION_TYPE_2G); + base::RunLoop().RunUntilIdle(); + + InitializeScheduler(); + } + ResourceScheduler* scheduler() { return scheduler_.get(); } base::test::ScopedTaskEnvironment scoped_task_environment_; @@ -2158,6 +2181,183 @@ high2_end_to_high3_start.InMilliseconds()); } +// Verify that when the proactive throttling is enabled, then delayable +// requests are throttled. +TEST_F(ResourceSchedulerTest, ProactiveThrottlingExperiment) { + const struct { + std::string test_case; + bool enable_http_rtt_multiplier_for_proactive_throttling; + } tests[] = { + { + "Enable proactive throttling", + true, + }, + { + "Disabled proactive throttling", + false, + }, + }; + + for (const auto& test : tests) { + double http_rtt_multiplier_for_proactive_throttling = 5; + base::TimeDelta http_rtt = base::TimeDelta::FromSeconds(1); + + if (test.enable_http_rtt_multiplier_for_proactive_throttling) { + ConfigureProactiveThrottlingExperimentFor2G( + http_rtt_multiplier_for_proactive_throttling); + } else { + ConfigureProactiveThrottlingExperimentFor2G(-1); + } + + network_quality_estimator_.SetStartTimeNullHttpRtt(http_rtt); + base::RunLoop().RunUntilIdle(); + + base::TimeDelta threshold_requests_anticipation = + http_rtt_multiplier_for_proactive_throttling * http_rtt; + + std::unique_ptr<TestRequest> high_1( + NewRequest("http://host/high_1", net::HIGHEST)); + EXPECT_TRUE(high_1->started()); + high_1.reset(); + + std::unique_ptr<TestRequest> low_1( + NewRequest("http://host/low_1", net::LOWEST)); + EXPECT_NE(test.enable_http_rtt_multiplier_for_proactive_throttling, + low_1->started()) + << " test_case=" << test.test_case; + + // Advancing the clock by a duration less than + // |threshold_requests_anticipation| should not cause low priority requests + // to start. + tick_clock_.Advance(threshold_requests_anticipation - + base::TimeDelta::FromMilliseconds(1)); + std::unique_ptr<TestRequest> low_2( + NewRequest("http://host/low_2", net::LOWEST)); + EXPECT_NE(test.enable_http_rtt_multiplier_for_proactive_throttling, + low_2->started()); + + // Advancing the clock by |threshold_requests_anticipation| should cause low + // priority requests to start. + tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100)); + std::unique_ptr<TestRequest> low_3( + NewRequest("http://host/low_3", net::LOWEST)); + EXPECT_TRUE(low_3->started()); + + // Verify that high priority requests are not throttled. + std::unique_ptr<TestRequest> high_2( + NewRequest("http://host/high_2", net::HIGHEST)); + EXPECT_TRUE(high_2->started()); + } +} + +// Verify that when the proactive throttling is enabled, then delayable +// requests are throttled, and the non-delayable requests are not throttled. +TEST_F(ResourceSchedulerTest, + ProactiveThrottlingDoesNotThrottleHighPriorityRequests) { + double http_rtt_multiplier_for_proactive_throttling = 5; + ConfigureProactiveThrottlingExperimentFor2G( + http_rtt_multiplier_for_proactive_throttling); + + base::TimeDelta http_rtt = base::TimeDelta::FromSeconds(1); + + network_quality_estimator_.SetStartTimeNullHttpRtt(http_rtt); + base::RunLoop().RunUntilIdle(); + + base::TimeDelta threshold_requests_anticipation = + http_rtt_multiplier_for_proactive_throttling * http_rtt; + + std::unique_ptr<TestRequest> high_1( + NewRequest("http://host/high_1", net::HIGHEST)); + EXPECT_TRUE(high_1->started()); + high_1.reset(); + + std::unique_ptr<TestRequest> low_1( + NewRequest("http://host/low_1", net::LOWEST)); + EXPECT_FALSE(low_1->started()); + + // Advancing the clock by a duration less than + // |threshold_requests_anticipation| should not cause low priority requests + // to start. + tick_clock_.Advance(threshold_requests_anticipation - + base::TimeDelta::FromMilliseconds(1)); + std::unique_ptr<TestRequest> low_2( + NewRequest("http://host/low_2", net::LOWEST)); + EXPECT_FALSE(low_2->started()); + + // Verify that high priority requests are not throttled. + std::unique_ptr<TestRequest> high_2( + NewRequest("http://host/high_2", net::HIGHEST)); + EXPECT_TRUE(high_2->started()); + high_2.reset(); + + // End existing requests. + low_1.reset(); + low_2.reset(); + + // Start a new high priority request. + std::unique_ptr<TestRequest> high_3( + NewRequest("http://host/high_3", net::HIGHEST)); + EXPECT_TRUE(high_3->started()); + high_3.reset(); + + // Verify that newly arriving delayalbe requests are still throttled. + std::unique_ptr<TestRequest> low_3( + NewRequest("http://host/low_3", net::LOWEST)); + EXPECT_FALSE(low_3->started()); + + // Current ECT is 2G. If another notification for ECT 2G is received, |low_3| + // is not started. + network_quality_estimator_.SetAndNotifyObserversOfEffectiveConnectionType( + net::EFFECTIVE_CONNECTION_TYPE_2G); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(low_3->started()); + + // Current ECT is 2G. If a notification for ECT 4G is received, |low_3| is + // started. + network_quality_estimator_.SetAndNotifyObserversOfEffectiveConnectionType( + net::EFFECTIVE_CONNECTION_TYPE_4G); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(low_3->started()); +} + +// Verify that when the proactive throttling is enabled, then delayable +// requests are throttled, and the non-delayable requests are not throttled. +TEST_F(ResourceSchedulerTest, ProactiveThrottling_UnthrottledOnTimerFired) { + double http_rtt_multiplier_for_proactive_throttling = 5; + ConfigureProactiveThrottlingExperimentFor2G( + http_rtt_multiplier_for_proactive_throttling); + + base::TimeDelta http_rtt = base::TimeDelta::FromSeconds(1); + + network_quality_estimator_.SetStartTimeNullHttpRtt(http_rtt); + base::RunLoop().RunUntilIdle(); + + base::TimeDelta threshold_requests_anticipation = + http_rtt_multiplier_for_proactive_throttling * http_rtt; + + std::unique_ptr<TestRequest> high_1( + NewRequest("http://host/high_1", net::HIGHEST)); + EXPECT_TRUE(high_1->started()); + high_1.reset(); + + std::unique_ptr<TestRequest> low_1( + NewRequest("http://host/low_1", net::LOWEST)); + EXPECT_FALSE(low_1->started()); + + // Advancing the clock by a duration less than + // |threshold_requests_anticipation| should not cause low priority requests + // to start. + tick_clock_.Advance(threshold_requests_anticipation + + base::TimeDelta::FromMilliseconds(1)); + + // Since the requests have been queued for too long, they should now be + // dispatched. Trigger the scheduling of the queued requests by calling + // DispatchLongQueuedRequestsForTesting(). + scheduler()->DispatchLongQueuedRequestsForTesting(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(low_1->started()); +} + } // unnamed namespace } // namespace network
diff --git a/services/service_manager/sandbox/BUILD.gn b/services/service_manager/sandbox/BUILD.gn index 032035d..0121475 100644 --- a/services/service_manager/sandbox/BUILD.gn +++ b/services/service_manager/sandbox/BUILD.gn
@@ -108,6 +108,7 @@ ] deps += [ + "//third_party/fuchsia-sdk/sdk:deprecatedtimezone", "//third_party/fuchsia-sdk/sdk:fonts", "//third_party/fuchsia-sdk/sdk:logger", "//third_party/fuchsia-sdk/sdk:mediacodec",
diff --git a/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc b/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc index 54e267b..35b86fe 100644 --- a/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc +++ b/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc
@@ -8,6 +8,7 @@ #include <stdio.h> #include <zircon/processargs.h> +#include <fuchsia/deprecatedtimezone/cpp/fidl.h> #include <fuchsia/fonts/cpp/fidl.h> #include <fuchsia/logger/cpp/fidl.h> #include <fuchsia/mediacodec/cpp/fidl.h> @@ -108,8 +109,9 @@ } // Services that are passed to all processes. -constexpr base::span<const char* const> kDefaultServices = - base::make_span((const char* const[]){fuchsia::logger::LogSink::Name_}); +constexpr base::span<const char* const> kDefaultServices = base::make_span( + (const char* const[]){fuchsia::deprecatedtimezone::Timezone::Name_, + fuchsia::logger::LogSink::Name_}); } // namespace
diff --git a/services/shape_detection/android/java/src/org/chromium/shape_detection/BarcodeDetectionImpl.java b/services/shape_detection/android/java/src/org/chromium/shape_detection/BarcodeDetectionImpl.java index 8521cea1..ba42205 100644 --- a/services/shape_detection/android/java/src/org/chromium/shape_detection/BarcodeDetectionImpl.java +++ b/services/shape_detection/android/java/src/org/chromium/shape_detection/BarcodeDetectionImpl.java
@@ -143,9 +143,9 @@ case Barcode.EAN_13: return BarcodeFormat.EAN_13; case Barcode.EAN_8: - return BarcodeFormat.CODE_128; - case Barcode.ITF: return BarcodeFormat.EAN_8; + case Barcode.ITF: + return BarcodeFormat.ITF; case Barcode.QR_CODE: return BarcodeFormat.QR_CODE; case Barcode.UPC_A:
diff --git a/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java b/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java index cb7dec1..c867c9a 100644 --- a/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java +++ b/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java
@@ -10,7 +10,12 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.params.BlockJUnit4RunnerDelegate; +import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter; +import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate; +import org.chromium.base.test.params.ParameterProvider; +import org.chromium.base.test.params.ParameterSet; +import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.Feature; import org.chromium.shape_detection.mojom.BarcodeDetection; import org.chromium.shape_detection.mojom.BarcodeDetectionProvider; @@ -18,13 +23,16 @@ import org.chromium.shape_detection.mojom.BarcodeDetectorOptions; import org.chromium.shape_detection.mojom.BarcodeFormat; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; /** * Test suite for BarcodeDetectionImpl. */ -@RunWith(BaseJUnit4ClassRunner.class) +@RunWith(ParameterizedRunner.class) +@UseRunnerDelegate(BlockJUnit4RunnerDelegate.class) public class BarcodeDetectionImplTest { private static final org.chromium.skia.mojom.Bitmap QR_CODE_BITMAP = TestUtils.mojoBitmapFromFile("qr_code.png"); @@ -100,38 +108,94 @@ Assert.assertArrayEquals(SUPPORTED_FORMATS, results); } - @Test - @SmallTest - @Feature({"ShapeDetection"}) - public void testDetectQrCodeWithHint() { - if (!TestUtils.IS_GMS_CORE_SUPPORTED) { - return; + public static class BarcodeExampleParams implements ParameterProvider { + private static List<ParameterSet> sBarcodeExampleParams = Arrays.asList( + new ParameterSet() + .value("aztec.png", BarcodeFormat.AZTEC, "Chromium", 11, 11, 61, 61) + .name("AZTEC"), + new ParameterSet() + .value("codabar.png", BarcodeFormat.CODABAR, "A6.2831853B", 24, 24, 448, 95) + .name("CODABAR"), + new ParameterSet() + .value("code_39.png", BarcodeFormat.CODE_39, "CHROMIUM", 20, 20, 318, 75) + .name("CODE_39"), + new ParameterSet() + .value("code_93.png", BarcodeFormat.CODE_93, "CHROMIUM", 20, 20, 216, 75) + .name("CODE_93"), + new ParameterSet() + .value("code_128.png", BarcodeFormat.CODE_128, "Chromium", 20, 20, 246, 75) + .name("CODE_128"), + new ParameterSet() + .value("data_matrix.png", BarcodeFormat.DATA_MATRIX, "Chromium", 11, 11, 53, + 53) + .name("DATA_MATIX"), + new ParameterSet() + .value("ean_8.png", BarcodeFormat.EAN_8, "62831857", 14, 10, 132, 75) + .name("EAN_8"), + new ParameterSet() + .value("ean_13.png", BarcodeFormat.EAN_13, "6283185307179", 27, 10, 188, 75) + .name("EAN_13"), + new ParameterSet() + .value("itf.png", BarcodeFormat.ITF, "62831853071795", 10, 10, 135, 39) + .name("ITF"), + new ParameterSet() + .value("pdf417.png", BarcodeFormat.PDF417, "Chromium", 20, 20, 240, 44) + .name("PDF417"), + new ParameterSet() + .value("qr_code.png", BarcodeFormat.QR_CODE, "https://chromium.org", 40, 40, + 250, 250) + .name("QR_CODE"), + new ParameterSet() + .value("upc_a.png", BarcodeFormat.UPC_A, "628318530714", 23, 10, 188, 75) + .name("UPC_A"), + new ParameterSet() + .value("upc_e.png", BarcodeFormat.UPC_E, "06283186", 23, 10, 100, 75) + .name("UPC_E")); + + @Override + public List<ParameterSet> getParameters() { + return sBarcodeExampleParams; } - BarcodeDetectionResult[] results = detectWithHint(QR_CODE_BITMAP, BarcodeFormat.QR_CODE); - Assert.assertEquals(1, results.length); - Assert.assertEquals("https://chromium.org", results[0].rawValue); - Assert.assertEquals(40.0, results[0].boundingBox.x, 0.0); - Assert.assertEquals(40.0, results[0].boundingBox.y, 0.0); - Assert.assertEquals(250.0, results[0].boundingBox.width, 0.0); - Assert.assertEquals(250.0, results[0].boundingBox.height, 0.0); - Assert.assertEquals(BarcodeFormat.QR_CODE, results[0].format); } @Test @SmallTest + @UseMethodParameter(BarcodeExampleParams.class) @Feature({"ShapeDetection"}) - public void testDetectQrCodeWithoutHint() { + public void testDetectBarcodeWithHint(String inputFile, int format, String value, float x, + float y, float width, float height) { if (!TestUtils.IS_GMS_CORE_SUPPORTED) { return; } - BarcodeDetectionResult[] results = detect(QR_CODE_BITMAP); + org.chromium.skia.mojom.Bitmap bitmap = TestUtils.mojoBitmapFromFile(inputFile); + BarcodeDetectionResult[] results = detectWithHint(bitmap, format); Assert.assertEquals(1, results.length); - Assert.assertEquals("https://chromium.org", results[0].rawValue); - Assert.assertEquals(40.0, results[0].boundingBox.x, 0.0); - Assert.assertEquals(40.0, results[0].boundingBox.y, 0.0); - Assert.assertEquals(250.0, results[0].boundingBox.width, 0.0); - Assert.assertEquals(250.0, results[0].boundingBox.height, 0.0); - Assert.assertEquals(BarcodeFormat.QR_CODE, results[0].format); + Assert.assertEquals(value, results[0].rawValue); + Assert.assertEquals(x, results[0].boundingBox.x, 0.0); + Assert.assertEquals(y, results[0].boundingBox.y, 0.0); + Assert.assertEquals(width, results[0].boundingBox.width, 0.0); + Assert.assertEquals(height, results[0].boundingBox.height, 0.0); + Assert.assertEquals(format, results[0].format); + } + + @Test + @SmallTest + @UseMethodParameter(BarcodeExampleParams.class) + @Feature({"ShapeDetection"}) + public void testDetectBarcodeWithoutHint(String inputFile, int format, String value, float x, + float y, float width, float height) { + if (!TestUtils.IS_GMS_CORE_SUPPORTED) { + return; + } + org.chromium.skia.mojom.Bitmap bitmap = TestUtils.mojoBitmapFromFile(inputFile); + BarcodeDetectionResult[] results = detect(bitmap); + Assert.assertEquals(1, results.length); + Assert.assertEquals(value, results[0].rawValue); + Assert.assertEquals(x, results[0].boundingBox.x, 0.0); + Assert.assertEquals(y, results[0].boundingBox.y, 0.0); + Assert.assertEquals(width, results[0].boundingBox.width, 0.0); + Assert.assertEquals(height, results[0].boundingBox.height, 0.0); + Assert.assertEquals(format, results[0].format); } @Test
diff --git a/services/test/data/aztec.png b/services/test/data/aztec.png new file mode 100644 index 0000000..2f7b656 --- /dev/null +++ b/services/test/data/aztec.png Binary files differ
diff --git a/services/test/data/codabar.png b/services/test/data/codabar.png new file mode 100644 index 0000000..0f54adf4 --- /dev/null +++ b/services/test/data/codabar.png Binary files differ
diff --git a/services/test/data/code_128.png b/services/test/data/code_128.png new file mode 100644 index 0000000..1eee0d3 --- /dev/null +++ b/services/test/data/code_128.png Binary files differ
diff --git a/services/test/data/code_39.png b/services/test/data/code_39.png new file mode 100644 index 0000000..1cf54f4 --- /dev/null +++ b/services/test/data/code_39.png Binary files differ
diff --git a/services/test/data/code_93.png b/services/test/data/code_93.png new file mode 100644 index 0000000..76c7714a --- /dev/null +++ b/services/test/data/code_93.png Binary files differ
diff --git a/services/test/data/data_matrix.png b/services/test/data/data_matrix.png new file mode 100644 index 0000000..f8563be --- /dev/null +++ b/services/test/data/data_matrix.png Binary files differ
diff --git a/services/test/data/ean_13.png b/services/test/data/ean_13.png new file mode 100644 index 0000000..bb7249d --- /dev/null +++ b/services/test/data/ean_13.png Binary files differ
diff --git a/services/test/data/ean_8.png b/services/test/data/ean_8.png new file mode 100644 index 0000000..76d7d5af --- /dev/null +++ b/services/test/data/ean_8.png Binary files differ
diff --git a/services/test/data/itf.png b/services/test/data/itf.png new file mode 100644 index 0000000..5a1463f --- /dev/null +++ b/services/test/data/itf.png Binary files differ
diff --git a/services/test/data/pdf417.png b/services/test/data/pdf417.png new file mode 100644 index 0000000..4b69548 --- /dev/null +++ b/services/test/data/pdf417.png Binary files differ
diff --git a/services/test/data/upc_a.png b/services/test/data/upc_a.png new file mode 100644 index 0000000..4c11f6e2 --- /dev/null +++ b/services/test/data/upc_a.png Binary files differ
diff --git a/services/test/data/upc_e.png b/services/test/data/upc_e.png new file mode 100644 index 0000000..8489c542 --- /dev/null +++ b/services/test/data/upc_e.png Binary files differ
diff --git a/services/viz/public/cpp/compositing/compositor_frame_struct_traits.cc b/services/viz/public/cpp/compositing/compositor_frame_struct_traits.cc index e824df34..11e36a46 100644 --- a/services/viz/public/cpp/compositing/compositor_frame_struct_traits.cc +++ b/services/viz/public/cpp/compositing/compositor_frame_struct_traits.cc
@@ -4,15 +4,11 @@ #include "services/viz/public/cpp/compositing/compositor_frame_struct_traits.h" -#include "base/trace_event/trace_event.h" - namespace mojo { // static bool StructTraits<viz::mojom::CompositorFrameDataView, viz::CompositorFrame>:: Read(viz::mojom::CompositorFrameDataView data, viz::CompositorFrame* out) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "StructTraits::CompositorFrame::Read"); return data.ReadPasses(&out->render_pass_list) && !out->render_pass_list.empty() && !out->render_pass_list.back()->output_rect.size().IsEmpty() &&
diff --git a/services/viz/public/cpp/compositing/render_pass_struct_traits.cc b/services/viz/public/cpp/compositing/render_pass_struct_traits.cc index 54627b1b..fb2c1b8b 100644 --- a/services/viz/public/cpp/compositing/render_pass_struct_traits.cc +++ b/services/viz/public/cpp/compositing/render_pass_struct_traits.cc
@@ -5,7 +5,6 @@ #include "services/viz/public/cpp/compositing/render_pass_struct_traits.h" #include "base/numerics/safe_conversions.h" -#include "base/trace_event/trace_event.h" #include "ui/gfx/mojo/color_space_mojom_traits.h" namespace mojo { @@ -15,8 +14,6 @@ std::unique_ptr<viz::RenderPass>>:: Read(viz::mojom::RenderPassDataView data, std::unique_ptr<viz::RenderPass>* out) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "StructTraits::RenderPass::Read"); *out = viz::RenderPass::Create(); if (!data.ReadOutputRect(&(*out)->output_rect) || !data.ReadDamageRect(&(*out)->damage_rect) ||
diff --git a/services/viz/public/cpp/compositing/transferable_resource_struct_traits.cc b/services/viz/public/cpp/compositing/transferable_resource_struct_traits.cc index ef5bafe..047e629 100644 --- a/services/viz/public/cpp/compositing/transferable_resource_struct_traits.cc +++ b/services/viz/public/cpp/compositing/transferable_resource_struct_traits.cc
@@ -4,7 +4,6 @@ #include "services/viz/public/cpp/compositing/transferable_resource_struct_traits.h" -#include "base/trace_event/trace_event.h" #include "gpu/ipc/common/mailbox_holder_struct_traits.h" #include "gpu/ipc/common/mailbox_struct_traits.h" #include "gpu/ipc/common/sync_token_struct_traits.h" @@ -18,8 +17,6 @@ viz::TransferableResource>:: Read(viz::mojom::TransferableResourceDataView data, viz::TransferableResource* out) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "StructTraits::TransferableResource::Read"); if (!data.ReadSize(&out->size) || !data.ReadMailboxHolder(&out->mailbox_holder) || !data.ReadColorSpace(&out->color_space))
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json index 00045b9a..b91b9b0 100644 --- a/testing/buildbot/chrome.json +++ b/testing/buildbot/chrome.json
@@ -796,14 +796,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", + "--disable-features=VizDisplayCompositor", "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -1038,6 +1038,27 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-14.04", + "pool": "chrome.tests" + } + ], + "shards": 10 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*" ], @@ -1058,27 +1079,6 @@ "test": "content_browsertests" }, { - "args": [ - "--enable-features=VizDisplayCompositor" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-14.04", - "pool": "chrome.tests" - } - ], - "shards": 10 - }, - "test": "content_browsertests" - }, - { "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -1096,13 +1096,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 2a77f891..9fe8640 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -938,13 +938,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -1162,6 +1162,26 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-16.04" + } + ], + "shards": 10 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*" ], @@ -1181,26 +1201,6 @@ "test": "content_browsertests" }, { - "args": [ - "--enable-features=VizDisplayCompositor" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-16.04" - } - ], - "shards": 10 - }, - "test": "content_browsertests" - }, - { "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -1217,13 +1217,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -2426,13 +2426,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -2650,6 +2650,26 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-16.04" + } + ], + "shards": 10 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*" ], @@ -2669,26 +2689,6 @@ "test": "content_browsertests" }, { - "args": [ - "--enable-features=VizDisplayCompositor" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-16.04" - } - ], - "shards": 10 - }, - "test": "content_browsertests" - }, - { "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -2705,13 +2705,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 19c11a1..33c7b91c 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -805,13 +805,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "shards": 10 @@ -859,13 +859,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_browsertests", + "name": "non_viz_content_browsertests", "swarming": { "can_use_on_swarming_builders": true, "shards": 10 @@ -874,13 +874,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true }, @@ -917,13 +917,13 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_interactive_ui_tests", + "name": "non_viz_interactive_ui_tests", "swarming": { "can_use_on_swarming_builders": true }, @@ -11526,14 +11526,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "isolate_coverage_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -11764,6 +11764,27 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor" + ], + "isolate_coverage_data": true, + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-14.04" + } + ], + "shards": 10 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*" ], @@ -11784,27 +11805,6 @@ "test": "content_browsertests" }, { - "args": [ - "--enable-features=VizDisplayCompositor" - ], - "isolate_coverage_data": true, - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-14.04" - } - ], - "shards": 10 - }, - "test": "content_browsertests" - }, - { "isolate_coverage_data": true, "merge": { "args": [], @@ -11822,14 +11822,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "isolate_coverage_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -13125,14 +13125,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "isolate_coverage_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -13363,6 +13363,27 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor" + ], + "isolate_coverage_data": true, + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-14.04" + } + ], + "shards": 10 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*" ], @@ -13383,27 +13404,6 @@ "test": "content_browsertests" }, { - "args": [ - "--enable-features=VizDisplayCompositor" - ], - "isolate_coverage_data": true, - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-14.04" - } - ], - "shards": 10 - }, - "test": "content_browsertests" - }, - { "isolate_coverage_data": true, "merge": { "args": [], @@ -13421,14 +13421,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor" + "--disable-features=VizDisplayCompositor" ], "isolate_coverage_data": true, "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 16a627d..4a0dd5f 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -6437,14 +6437,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", + "--disable-features=VizDisplayCompositor", "--test-launcher-print-test-stdio=always" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -6698,6 +6698,27 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor", + "--test-launcher-print-test-stdio=always" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-14.04" + } + ], + "shards": 4 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*", "--test-launcher-print-test-stdio=always" @@ -6719,27 +6740,6 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "os": "Ubuntu-14.04" - } - ], - "shards": 4 - }, - "test": "content_browsertests" - }, - { - "args": [ "--test-launcher-print-test-stdio=always" ], "merge": { @@ -6758,14 +6758,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", + "--disable-features=VizDisplayCompositor", "--test-launcher-print-test-stdio=always" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -8212,14 +8212,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", + "--disable-features=VizDisplayCompositor", "--test-launcher-print-test-stdio=always" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_browser_tests", + "name": "non_viz_browser_tests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [ @@ -8486,6 +8486,28 @@ }, { "args": [ + "--disable-features=VizDisplayCompositor", + "--test-launcher-print-test-stdio=always" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "non_viz_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-14.04" + } + ], + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "args": [ "--disable-perfetto", "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*", "--test-launcher-print-test-stdio=always" @@ -8508,28 +8530,6 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", - "--test-launcher-print-test-stdio=always" - ], - "merge": { - "args": [], - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "name": "viz_content_browsertests", - "swarming": { - "can_use_on_swarming_builders": true, - "dimension_sets": [ - { - "cpu": "x86-64", - "os": "Ubuntu-14.04" - } - ], - "shards": 2 - }, - "test": "content_browsertests" - }, - { - "args": [ "--test-launcher-print-test-stdio=always" ], "merge": { @@ -8550,14 +8550,14 @@ }, { "args": [ - "--enable-features=VizDisplayCompositor", + "--disable-features=VizDisplayCompositor", "--test-launcher-print-test-stdio=always" ], "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" }, - "name": "viz_content_unittests", + "name": "non_viz_content_unittests", "swarming": { "can_use_on_swarming_builders": true, "dimension_sets": [
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 067a90b..0d68dfd 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1154,6 +1154,67 @@ }, }, }, + 'non_viz_browser_tests': { + 'modifications': { + # chromium.fyi + 'linux-chromeos-code-coverage': { + 'swarming': { + 'shards': 20, + }, + }, + 'linux-chromeos-google-rel': { + # TODO(https://crbug.com/932269): Promote out of experiment when the + # tests are green. + 'args': [ + '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter', + ], + }, + 'linux-chromeos-oobe-code-coverage': { + 'swarming': { + 'shards': 20, + }, + }, + # chromium.chromiumos + 'linux-chromeos-dbg': { + 'swarming': { + 'shards': 20, + }, + }, + # chromium.memory + 'Linux ChromiumOS MSan Tests': { + # These are very slow on the Chrome OS MSAN trybot for some reason. + # crbug.com/865455 + 'swarming': { + 'shards': 30, + }, + }, + 'Linux Chromium OS ASan LSan Tests (1)': { + # These are very slow on the ASAN trybot for some reason. + # crbug.com/794372 + 'swarming': { + 'shards': 30, + }, + }, + }, + }, + 'non_viz_content_browsertests': { + 'modifications': { + # chromium.memory + 'Linux Chromium OS ASan LSan Tests (1)': { + # content_browsertests is slow on ASAN try bot. crbug.com/822461. + 'swarming': { + 'shards': 4, + } + }, + 'Linux ChromiumOS MSan Tests': { + # These are very slow on the Chrome OS MSAN trybot for some reason. + # crbug.com/865455 + 'swarming': { + 'shards': 2, + }, + }, + }, + }, 'nonperfetto_content_browsertests': { 'remove_from': [ 'android-asan', @@ -1573,67 +1634,6 @@ 'Win10 FYI Exp Release (Intel HD 630)', ], }, - 'viz_browser_tests': { - 'modifications': { - # chromium.fyi - 'linux-chromeos-code-coverage': { - 'swarming': { - 'shards': 20, - }, - }, - 'linux-chromeos-google-rel': { - # TODO(https://crbug.com/932269): Promote out of experiment when the - # tests are green. - 'args': [ - '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.browser_tests.filter', - ], - }, - 'linux-chromeos-oobe-code-coverage': { - 'swarming': { - 'shards': 20, - }, - }, - # chromium.chromiumos - 'linux-chromeos-dbg': { - 'swarming': { - 'shards': 20, - }, - }, - # chromium.memory - 'Linux ChromiumOS MSan Tests': { - # These are very slow on the Chrome OS MSAN trybot for some reason. - # crbug.com/865455 - 'swarming': { - 'shards': 30, - }, - }, - 'Linux Chromium OS ASan LSan Tests (1)': { - # These are very slow on the ASAN trybot for some reason. - # crbug.com/794372 - 'swarming': { - 'shards': 30, - }, - }, - }, - }, - 'viz_content_browsertests': { - 'modifications': { - # chromium.memory - 'Linux Chromium OS ASan LSan Tests (1)': { - # content_browsertests is slow on ASAN try bot. crbug.com/822461. - 'swarming': { - 'shards': 4, - } - }, - 'Linux ChromiumOS MSan Tests': { - # These are very slow on the Chrome OS MSAN trybot for some reason. - # crbug.com/865455 - 'swarming': { - 'shards': 2, - }, - }, - }, - }, 'viz_unittests': { 'remove_from': [ # chromium.win
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 719b40be..bd07621 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -4675,43 +4675,37 @@ }, 'viz_chromeos_fyi_gtests': { - 'viz_content_unittests': { - 'args': [ - '--enable-features=VizDisplayCompositor', - ], - 'test': 'content_unittests', - }, - 'viz_interactive_ui_tests': { + 'non_viz_interactive_ui_tests': { 'args': [ - '--enable-features=VizDisplayCompositor', + '--disable-features=VizDisplayCompositor', ], 'test': 'interactive_ui_tests', }, }, 'viz_chromeos_gtests': { - # TODO(jonross): remove once Viz field trials for ChromeOS are enabled. - 'viz_browser_tests': { + # TODO(kylechar): Remove once VizDisplayCompositor is lauched on CrOS. + 'non_viz_browser_tests': { 'args': [ - '--enable-features=VizDisplayCompositor', + '--disable-features=VizDisplayCompositor', ], 'swarming': { 'shards': 10, }, 'test': 'browser_tests', }, - 'viz_content_browsertests': { + 'non_viz_content_browsertests': { 'args': [ - '--enable-features=VizDisplayCompositor', + '--disable-features=VizDisplayCompositor', ], 'swarming': { 'shards': 10, }, 'test': 'content_browsertests', }, - 'viz_content_unittests': { + 'non_viz_content_unittests': { 'args': [ - '--enable-features=VizDisplayCompositor', + '--disable-features=VizDisplayCompositor', ], 'test': 'content_unittests', },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 2ab43b5..b1fef5b4 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -4290,6 +4290,21 @@ ] } ], + "ReorderBookmarks": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "ReorderBookmarks", + "enable_features": [ + "ReorderBookmarks" + ] + } + ] + } + ], "ReportCertificateErrors": [ { "platforms": [
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd index fbbdcaf..119dda39 100644 --- a/third_party/blink/public/blink_resources.grd +++ b/third_party/blink/public/blink_resources.grd
@@ -52,8 +52,11 @@ <include name="IDR_PICKER_BUTTON_CSS" file="../renderer/core/html/forms/resources/pickerButton.css" type="BINDATA" compress="gzip"/> <include name="IDR_SUGGESTION_PICKER_CSS" file="../renderer/core/html/forms/resources/suggestionPicker.css" type="BINDATA" compress="gzip"/> <include name="IDR_SUGGESTION_PICKER_JS" file="../renderer/core/html/forms/resources/suggestionPicker.js" type="BINDATA" compress="gzip"/> + <include name="IDR_COLOR_PICKER_COMMON_JS" file="../renderer/core/html/forms/resources/color_picker_common.js" type="BINDATA" compress="gzip"/> <include name="IDR_COLOR_SUGGESTION_PICKER_CSS" file="../renderer/core/html/forms/resources/colorSuggestionPicker.css" type="BINDATA" compress="gzip"/> <include name="IDR_COLOR_SUGGESTION_PICKER_JS" file="../renderer/core/html/forms/resources/colorSuggestionPicker.js" type="BINDATA" compress="gzip"/> + <include name="IDR_COLOR_PICKER_CSS" file="../renderer/core/html/forms/resources/color_picker.css" type="BINDATA" compress="gzip"/> + <include name="IDR_COLOR_PICKER_JS" file="../renderer/core/html/forms/resources/color_picker.js" type="BINDATA" compress="gzip"/> <include name="IDR_LIST_PICKER_CSS" file="../renderer/core/html/forms/resources/listPicker.css" type="BINDATA" compress="gzip"/> <include name="IDR_LIST_PICKER_JS" file="../renderer/core/html/forms/resources/listPicker.js" type="BINDATA" compress="gzip"/> </if>
diff --git a/third_party/blink/public/web/web_form_control_element.h b/third_party/blink/public/web/web_form_control_element.h index 7ff76101..6fae259a 100644 --- a/third_party/blink/public/web/web_form_control_element.h +++ b/third_party/blink/public/web/web_form_control_element.h
@@ -135,6 +135,10 @@ // overflow. unsigned UniqueRendererFormControlId() const; + // Returns the ax node id of the form control element in the accessibility + // tree. The ax node id is consistent across renderer and browser processes. + int32_t GetAxId() const; + #if INSIDE_BLINK WebFormControlElement(HTMLFormControlElement*); WebFormControlElement& operator=(HTMLFormControlElement*);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index bcf9a85..df50f21 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2918,8 +2918,20 @@ // Plugins can run script inside layout which can detach the page. // TODO(dcheng): Does it make sense to do any of this work if detached? - if (GetFrame() && GetFrame()->IsMainFrame()) - GetFrame()->GetPage()->GetChromeClient().MainFrameLayoutUpdated(); + if (GetFrame()) { + if (GetFrame()->IsMainFrame()) + GetFrame()->GetPage()->GetChromeClient().MainFrameLayoutUpdated(); + + // We do attach here, during lifecycle update, because until then we + // don't have a good place that has access to its local root's FrameWidget. + // TODO(dcheng): If we create FrameWidget before Frame then we could move + // this to Document::Initialize(). + if (Platform::Current()->IsThreadedAnimationEnabled() && + GetSettings()->GetAcceleratedCompositingEnabled()) { + GetPage()->GetChromeClient().AttachCompositorAnimationTimeline( + Timeline().CompositorTimeline(), GetFrame()); + } + } Markers().InvalidateRectsForAllTextMatchMarkers(); @@ -3076,10 +3088,7 @@ layout_view_ = new LayoutView(this); SetLayoutObject(layout_view_); - layout_view_->SetIsInWindow(true); layout_view_->SetStyle(StyleResolver::StyleForViewport(*this)); - layout_view_->Compositor()->SetNeedsCompositingUpdate( - kCompositingUpdateAfterCompositingInputChange); AttachContext context; AttachLayoutTree(context); @@ -3099,15 +3108,6 @@ // attached to a frame. Otherwise ContextLifecycleObserver::contextDestroyed // wouldn't be fired. network_state_observer_ = MakeGarbageCollected<NetworkStateObserver>(*this); - - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - CompositorAnimationTimeline* animation_timeline = - Timeline().CompositorTimeline(); - if (animation_timeline) { - GetPage()->GetChromeClient().AttachCompositorAnimationTimeline( - animation_timeline, frame_.Get()); - } - } } void Document::Shutdown() { @@ -3168,15 +3168,7 @@ markers_->PrepareForDestruction(); - if (GetPage()) { - GetPage()->DocumentDetached(this); - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - if (auto* compositor_timeline = Timeline().CompositorTimeline()) { - GetPage()->GetChromeClient().DetachCompositorAnimationTimeline( - compositor_timeline, frame_.Get()); - } - } - } + GetPage()->DocumentDetached(this); probe::DocumentDetached(this); @@ -3193,8 +3185,13 @@ CancelPendingJavaScriptUrls(); http_refresh_scheduler_->Cancel(); - if (layout_view_) - layout_view_->SetIsInWindow(false); + if (Platform::Current()->IsThreadedAnimationEnabled() && + GetSettings()->GetAcceleratedCompositingEnabled()) { + GetPage()->GetChromeClient().DetachCompositorAnimationTimeline( + Timeline().CompositorTimeline(), GetFrame()); + } + + layout_view_->CleanUpCompositor(); if (RegistrationContext()) RegistrationContext()->DocumentWasDetached();
diff --git a/third_party/blink/renderer/core/exported/web_form_control_element.cc b/third_party/blink/renderer/core/exported/web_form_control_element.cc index f12170d72..9c623dc 100644 --- a/third_party/blink/renderer/core/exported/web_form_control_element.cc +++ b/third_party/blink/renderer/core/exported/web_form_control_element.cc
@@ -248,6 +248,10 @@ return ConstUnwrap<HTMLFormControlElement>()->UniqueRendererFormControlId(); } +int32_t WebFormControlElement::GetAxId() const { + return ConstUnwrap<HTMLFormControlElement>()->GetAxId(); +} + WebFormControlElement::WebFormControlElement(HTMLFormControlElement* elem) : WebElement(elem) {}
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index 734ee52..3fd295d 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -510,10 +510,15 @@ mojo::MakeRequest(&document_interface_broker).PassMessagePipe(), nullptr); web_frame_client.Bind(frame); - // We inform the WebView when it has a local main frame attached once the - // WebFrame it fully set up and the WebWidgetClient is initialized (which is - // the case by this point). - web_view->DidAttachLocalMainFrame(&web_widget_client); + { + // Copy the steps done from WebViewHelper::InitializeWithOpener() to set up + // the appropriate pointers! + web_view->MainFrameWidget()->SetLayerTreeView( + web_widget_client.layer_tree_view(), + web_widget_client.animation_host()); + blink::WebFrameWidget::CreateForMainFrame(&web_widget_client, frame); + web_view->DidAttachLocalMainFrame(&web_widget_client); + } // The color should be passed to the compositor. cc::LayerTreeHost* host = web_widget_client.layer_tree_host();
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc index 3187c0b..f81ad93 100644 --- a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc +++ b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
@@ -65,7 +65,8 @@ } void ColorChooserPopupUIController::OpenUI() { - if (client_->ShouldShowSuggestions()) + if (client_->ShouldShowSuggestions() || + RuntimeEnabledFeatures::FormControlsRefreshEnabled()) OpenPopup(); else OpenColorChooser(); @@ -81,6 +82,45 @@ } void ColorChooserPopupUIController::WriteDocument(SharedBuffer* data) { + if (client_->ShouldShowSuggestions()) { + WriteColorSuggestionPickerDocument(data); + } else { + WriteColorPickerDocument(data); + } +} + +void ColorChooserPopupUIController::WriteColorPickerDocument( + SharedBuffer* data) { + DCHECK(RuntimeEnabledFeatures::FormControlsRefreshEnabled()); + + IntRect anchor_rect_in_screen = chrome_client_->ViewportToScreen( + client_->ElementRectRelativeToViewport(), frame_->View()); + + PagePopupClient::AddString( + "<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data); + data->Append(Platform::Current()->GetDataResource("pickerCommon.css")); + data->Append(Platform::Current()->GetDataResource("color_picker.css")); + PagePopupClient::AddString( + "</style></head><body>\n" + "<div id='main'>Loading...</div><script>\n" + "window.dialogArguments = {\n", + data); + PagePopupClient::AddProperty("selectedColor", + client_->CurrentColor().Serialized(), data); + AddProperty("anchorRectInScreen", anchor_rect_in_screen, data); + AddProperty("zoomFactor", ZoomFactor(), data); + AddProperty("shouldShowColorSuggestionPicker", false, data); + PagePopupClient::AddString("};\n", data); + data->Append(Platform::Current()->GetDataResource("pickerCommon.js")); + data->Append(Platform::Current()->GetDataResource("color_picker.js")); + data->Append(Platform::Current()->GetDataResource("color_picker_common.js")); + PagePopupClient::AddString("</script></body>\n", data); +} + +void ColorChooserPopupUIController::WriteColorSuggestionPickerDocument( + SharedBuffer* data) { + DCHECK(client_->ShouldShowSuggestions()); + Vector<String> suggestion_values; for (auto& suggestion : client_->Suggestions()) suggestion_values.push_back(Color(suggestion->color).Serialized()); @@ -92,20 +132,35 @@ data->Append(Platform::Current()->GetDataResource("pickerCommon.css")); data->Append( Platform::Current()->GetDataResource("colorSuggestionPicker.css")); + if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + data->Append(Platform::Current()->GetDataResource("color_picker.css")); + } PagePopupClient::AddString( - "</style></head><body><div id=main>Loading...</div><script>\n" + "</style></head><body>\n" + "<div id='main'>Loading...</div><script>\n" "window.dialogArguments = {\n", data); PagePopupClient::AddProperty("values", suggestion_values, data); PagePopupClient::AddProperty( "otherColorLabel", GetLocale().QueryString(WebLocalizedString::kOtherColorLabel), data); + if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + PagePopupClient::AddProperty("selectedColor", + client_->CurrentColor().Serialized(), data); + } AddProperty("anchorRectInScreen", anchor_rect_in_screen, data); AddProperty("zoomFactor", ZoomFactor(), data); + AddProperty("shouldShowColorSuggestionPicker", true, data); + AddProperty("isFormControlsRefreshEnabled", + RuntimeEnabledFeatures::FormControlsRefreshEnabled(), data); PagePopupClient::AddString("};\n", data); data->Append(Platform::Current()->GetDataResource("pickerCommon.js")); data->Append( Platform::Current()->GetDataResource("colorSuggestionPicker.js")); + if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + data->Append(Platform::Current()->GetDataResource("color_picker.js")); + } + data->Append(Platform::Current()->GetDataResource("color_picker_common.js")); PagePopupClient::AddString("</script></body>\n", data); } @@ -120,8 +175,10 @@ DCHECK(client_); if (num_value == kColorPickerPopupActionSetValue) SetValue(string_value); - if (num_value == kColorPickerPopupActionChooseOtherColor) + if (num_value == kColorPickerPopupActionChooseOtherColor) { + DCHECK(!RuntimeEnabledFeatures::FormControlsRefreshEnabled()); OpenColorChooser(); + } CancelPopup(); }
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h index 26b8153..989446b 100644 --- a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h +++ b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h
@@ -69,6 +69,9 @@ void OpenPopup(); void Dispose(); + void WriteColorPickerDocument(SharedBuffer*); + void WriteColorSuggestionPickerDocument(SharedBuffer*); + Member<ChromeClient> chrome_client_; PagePopup* popup_; Locale& locale_;
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc index c5d86ab0..c9b2036 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -402,4 +402,11 @@ AssociateByParser(form); } +int32_t HTMLFormControlElement::GetAxId() const { + if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) + return cache->GetAXID(const_cast<HTMLFormControlElement*>(this)); + + return 0; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.h b/third_party/blink/renderer/core/html/forms/html_form_control_element.h index 62cfe64..df09c198 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_control_element.h +++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
@@ -130,6 +130,8 @@ return unique_renderer_form_control_id_; } + int32_t GetAxId() const; + protected: HTMLFormControlElement(const QualifiedName& tag_name, Document&);
diff --git a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css index eda7bbb..9a3e42d 100644 --- a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css +++ b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
@@ -31,7 +31,7 @@ overflow: hidden; } -#main { +.color-suggestion-picker-main { background-color: white; border: solid 1px #8899aa; box-shadow: inset 2px 2px 2px white,
diff --git a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js index 00b970f..15c60eb0 100644 --- a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js +++ b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js
@@ -24,49 +24,18 @@ * DAMAGE. */ -var global = {argumentsReceived: false, params: null}; -/** - * @param {Event} event - */ -function handleMessage(event) { - initialize(JSON.parse(event.data)); - global.argumentsReceived = true; -} - -/** - * @param {!Object} args - */ -function initialize(args) { - global.params = args; - var main = $('main'); - main.innerHTML = ''; - var errorString = validateArguments(args); - if (errorString) { - main.textContent = 'Internal error: ' + errorString; - resizeWindow(main.offsetWidth, main.offsetHeight); - } else - new ColorSuggestionPicker(main, args); -} - -// The DefaultColorPalette is used when the list of values are empty. -var DefaultColorPalette = [ - '#000000', '#404040', '#808080', '#c0c0c0', '#ffffff', '#980000', '#ff0000', '#ff9900', '#ffff00', '#00ff00', - '#00ffff', '#4a86e8', '#0000ff', '#9900ff', '#ff00ff' -]; - -function handleArgumentsTimeout() { - if (global.argumentsReceived) - return; - var args = {values: DefaultColorPalette, otherColorLabel: 'Other...'}; - initialize(args); +function initializeColorSuggestionPicker() { + new ColorSuggestionPicker(main, global.params); } /** * @param {!Object} args * @return {?string} An error message, or null if the argument has no errors. */ -function validateArguments(args) { +function validateColorSuggestionPickerArguments(args) { + if (!args.shouldShowColorSuggestionPicker) + return 'Should not be showing the color suggestion picker.' if (!args.values) return 'No values.'; if (!args.otherColorLabel) @@ -74,6 +43,13 @@ return null; } +function handleArgumentsTimeout() { + if (global.argumentsReceived) + return; + var args = {values: DefaultColorPalette, otherColorLabel: 'Other...'}; + initialize(args); +} + function ColorSuggestionPicker(element, config) { Picker.call(this, element, config); this._config = config; @@ -110,7 +86,7 @@ container.style.maxHeight = (SwatchBorderBoxHeight * SwatchesMaxRow) + 'px'; this._element.appendChild(container); var otherButton = createElement('button', 'other-color', this._config.otherColorLabel); - otherButton.addEventListener('click', this.chooseOtherColor.bind(this), false); + otherButton.addEventListener('click', this._onOtherButtonClick.bind(this), false); this._element.appendChild(otherButton); this._container = container; this._otherButton = otherButton; @@ -119,6 +95,22 @@ resizeWindow(elementWidth, elementHeight); }; +ColorSuggestionPicker.prototype._onOtherButtonClick = function() { + if (global.params.isFormControlsRefreshEnabled) { + var main = $('main'); + main.innerHTML = ''; + main.classList.remove('color-suggestion-picker-main'); + main.classList.add('color-picker-main'); + // Replace document.body with a deep clone to drop all event listeners. + var oldBody = document.body; + var newBody = oldBody.cloneNode(true); + oldBody.parentElement.replaceChild(newBody, oldBody); + initializeColorPicker(); + } else { + this.chooseOtherColor(); + } +} + ColorSuggestionPicker.prototype.selectColorAtIndex = function(index) { index = Math.max(Math.min(this._container.childNodes.length - 1, index), 0); this._container.childNodes[index].focus(); @@ -176,10 +168,3 @@ if (event.target.classList.contains('color-swatch')) this.submitValue(event.target.dataset.value); }; - -if (window.dialogArguments) { - initialize(dialogArguments); -} else { - window.addEventListener('message', handleMessage, false); - window.setTimeout(handleArgumentsTimeout, 1000); -}
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.css b/third_party/blink/renderer/core/html/forms/resources/color_picker.css new file mode 100644 index 0000000..1232585 --- /dev/null +++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.css
@@ -0,0 +1,99 @@ +/* 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. + */ + +body { + font-family: "Segoe UI", system-ui, Roboto, Ubuntu, sans-serif; +} + +color-picker { + background: #FFFFFF; + display: flex; + flex-direction: column; + height: 304px; + width: 232px; +} + +visual-color-picker { + height: 59%; +} + +manual-color-picker { + height: 28%; +} + +color-value-container { + display: flex; + flex-direction: row; + height: 32px; + margin-left: 10%; + margin-top: 2%; + width: 171px; +} + +input { + background: #FFFFFF; + border: 1px solid #CECECE; + border-radius: 2px; + box-sizing: border-box; + font-family: inherit; + font-size: 14px; + text-align: center; +} + +format-toggler { + border-radius: 2px; + display: block; + height: 24px; + margin-left: 10%; + margin-top: 2%; + width: 200px; +} + +format-toggler:hover { + background-color: #F7F7F7; +} + +format-label { + border-radius: 2px; + display: flex; + flex-direction: row; + font-family: inherit; + font-size: 12px; + height: 24px; + width: 172px; +} + +channel-label { + align-items: center; + display: flex; + flex-direction: row; + height: 24px; + justify-content: center; + width: 172px; +} + +submission-controls { + align-items: center; + border-top: 1px solid #CECECE; + display: flex; + flex-direction: row; + height: 13%; +} + +#submission-controls-padding { + height: 100%; + width: 84%; +} + +submission-button { + padding: 3%; + text-align: center; + width: 8%; +} + +submission-button:hover { + background-color: #F3F3F3; + border-radius: 2px; +} \ No newline at end of file
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js new file mode 100644 index 0000000..4bebefac --- /dev/null +++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -0,0 +1,437 @@ +// 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. + +/** + * @fileoverview Color picker used by <input type='color' /> + */ + +/** + * @param {!Object} args + */ +function initializeColorPicker() { + if (global.params.selectedColor === undefined) { + global.params.selectedColor = DefaultColor; + } + const colorPicker = new ColorPicker(new Color(global.params.selectedColor)); + main.append(colorPicker); + const width = colorPicker.offsetWidth; + const height = colorPicker.offsetHeight; + resizeWindow(width, height); +} + +/** + * @param {!Object} args + * @return {?string} An error message, or null if the argument has no errors. + */ +function validateColorPickerArguments(args) { + if (args.shouldShowColorSuggestionPicker) + return 'Should be showing the color suggestion picker.' + if (!args.selectedColor) + return 'No selectedColor.'; + return null; +} + +/** + * Supported color channels. + * @enum {number} + */ +const ColorChannel = { + UNDEFINED: 0, + HEX: 1, + R: 2, + G: 3, + B: 4, + H: 5, + S: 6, + L: 7, +}; + +/** + * Supported color formats. + * @enum {number} + */ +const ColorFormat = { + UNDEFINED: 0, + HEX: 1, + RGB: 2, + HSL: 3, +}; + +/** + * Color: Helper class to get color values in different color formats. + */ +class Color { + /** + * @param {string|!ColorFormat} colorStringOrFormat + * @param {...number} colorValues ignored if colorStringOrFormat is a string + */ + constructor(colorStringOrFormat, ...colorValues) { + if (typeof colorStringOrFormat === 'string') { + colorStringOrFormat = colorStringOrFormat.toLowerCase(); + if (colorStringOrFormat.startsWith('#')) { + this.hexValue_ = colorStringOrFormat.substr(1); + } + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } else { + switch(colorStringOrFormat) { + case ColorFormat.HEX: + this.hexValue_ = colorValues[0]; + break; + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } + } + } + + /** + * @param {!Color} other + */ + equals(other) { + return (this.hexValue_ === other.hexValue_); + } + + get hexValue() { + return this.hexValue_; + } + + asHex() { + return '#' + this.hexValue_; + } +} + +/** + * ColorPicker: Custom element providing a color picker implementation. + * A color picker is comprised of three main parts: a visual color + * picker to allow visual selection of colors, a manual color + * picker to allow numeric selection of colors, and submission + * controls to save/discard new color selections. + */ +class ColorPicker extends HTMLElement { + /** + * @param {!Color} initialColor + */ + constructor(initialColor) { + super(); + + this.selectedColor_ = initialColor; + + this.visualColorPicker_ = new VisualColorPicker(initialColor); + this.manualColorPicker_ = new ManualColorPicker(initialColor); + this.submissionControls_ = + new SubmissionControls(this.onSubmitButtonClick_, + this.onCancelButtonClick_); + this.append(this.visualColorPicker_, + this.manualColorPicker_, + this.submissionControls_); + + this.manualColorPicker_ + .addEventListener('manual-color-change', this.onManualColorChange_); + } + + get selectedColor() { + return this.selectedColor_; + } + + /** + * @param {!Color} newColor + */ + set selectedColor(newColor) { + this.selectedColor_ = newColor; + } + + /** + * @param {!Event} event + */ + onManualColorChange_ = (event) => { + var newColor = event.detail.color; + if (!this.selectedColor.equals(newColor)) { + this.selectedColor = newColor; + this.visualColorPicker_.setSelectedColor(newColor); + } + } + + onSubmitButtonClick_ = () => { + const selectedValue = this.selectedColor_.asHex(); + window.setTimeout(function() { + window.pagePopupController.setValueAndClosePopup(0, selectedValue); + }, 100); + } + + onCancelButtonClick_ = () => { + window.pagePopupController.closePopup(); + } +} +window.customElements.define('color-picker', ColorPicker); + +/** + * VisualColorPicker: Provides functionality to see the selected color and + * select a different color visually. + * TODO(crbug.com/983311): Allow colors to be selected from within the visual + * color picker. + */ +class VisualColorPicker extends HTMLElement { + /** + * @param {!Color} initialColor + */ + constructor(initialColor) { + super(); + + this.setSelectedColor(initialColor); + } + + /** + * @param {!Color} newColor + */ + setSelectedColor(newColor) { + this.style.backgroundColor = newColor.asHex(); + } +} +window.customElements.define('visual-color-picker', VisualColorPicker); + +/** + * ManualColorPicker: Provides functionality to change the selected color by + * manipulating its numeric values. + */ +class ManualColorPicker extends HTMLElement { + /** + * @param {!Color} initialColor + */ + constructor(initialColor) { + super(); + + this.colorValueContainers_ = [ + new ColorValueContainer(ColorFormat.HEX, + initialColor) + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + ]; + this.formatToggler_ = new FormatToggler(); + this.append(...this.colorValueContainers_, this.formatToggler_); + } +} +window.customElements.define('manual-color-picker', ManualColorPicker); + +/** + * ColorValueContainer: Maintains a set of channel values that make up a given + * color format, and tracks value changes. + */ +class ColorValueContainer extends HTMLElement { + /** + * @param {!ColorFormat} colorFormat + * @param {!Color} initialColor + */ + constructor(colorFormat, initialColor) { + super(); + + this.colorFormat_ = colorFormat; + this.channelValueContainers_ = []; + if (this.colorFormat_ === ColorFormat.HEX) { + const hexValueContainer = + new ChannelValueContainer(ColorChannel.HEX, + initialColor); + this.channelValueContainers_.push(hexValueContainer); + } + this.append(...this.channelValueContainers_); + + this.channelValueContainers_.forEach((channelValueContainer) => + channelValueContainer.addEventListener('input', + this.onChannelValueChange_)); + } + + get color() { + return new Color(this.colorFormat_, + ...this.channelValueContainers_.map((channelValueContainer) => + channelValueContainer.channelValue)); + } + + onChannelValueChange_ = () => { + this.dispatchEvent(new CustomEvent('manual-color-change', { + bubbles: true, + detail: { + color: this.color + } + })); + } +} +window.customElements.define('color-value-container', ColorValueContainer); + +/** + * ChannelValueContainer: Maintains and displays the numeric value + * for a given color channel. + */ +class ChannelValueContainer extends HTMLInputElement { + /** + * @param {!ColorChannel} colorChannel + * @param {!Color} initialColor + */ + constructor(colorChannel, initialColor) { + super(); + + this.setAttribute('type', 'text'); + this.colorChannel_ = colorChannel; + switch(colorChannel) { + case ColorChannel.HEX: + this.setAttribute('maxlength', '7'); + break; + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } + this.setValue(initialColor); + + this.addEventListener('input', this.onValueChange_); + } + + get channelValue() { + return this.channelValue_; + } + + /** + * @param {!Color} color + */ + setValue(color) { + switch(this.colorChannel_) { + case ColorChannel.HEX: + this.channelValue_ = color.hexValue; + this.value = '#' + this.channelValue_; + break; + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } + } + + onValueChange_ = () => { + // Set this.channelValue_ based on the element's new value. + let value = this.value; + if (value) { + switch(this.colorChannel_) { + case ColorChannel.HEX: + if (value.startsWith('#')) { + value = value.substr(1); + if (value.match(/^[0-9a-fA-F]+$/)) { + // Ex. 'FFFFFF' => this.channelValue_ == 'FFFFFF' + // Ex. 'FF' => this.channelValue_ == '0000FF' + this.channelValue_ = ('000000' + value).slice(-6); + } + } + break; + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } + } + } +} +window.customElements.define('channel-value-container', + ChannelValueContainer, + { extends: 'input' }); + +/** + * FormatToggler: Button that powers switching between different color formats. + */ +class FormatToggler extends HTMLElement { + constructor() { + super(); + + this.colorFormatLabels_ = [ + new FormatLabel(ColorFormat.HEX) + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + ]; + this.append(...this.colorFormatLabels_); + } +} +window.customElements.define('format-toggler', FormatToggler); + +/** + * FormatLabel: Label for a given color format. + */ +class FormatLabel extends HTMLElement { + /** + * @param {!ColorFormat} colorFormat + */ + constructor(colorFormat) { + super(); + + if (colorFormat === ColorFormat.HEX) { + this.hexChannelLabel_ = new ChannelLabel(ColorChannel.HEX); + this.append(this.hexChannelLabel_); + } + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } +} +window.customElements.define('format-label', FormatLabel); + +/** + * ChannelLabel: Label for a color channel, to be used within a FormatLabel. + */ +class ChannelLabel extends HTMLElement { + /** + * @param {!ColorChannel} colorChannel + */ + constructor(colorChannel) { + super(); + + if (colorChannel === ColorChannel.HEX) { + this.textContent = 'HEX'; + } + // TODO(crbug.com/982087): Add support for RGB + // TODO(crbug.com/982088): Add support for HSL + } +} +window.customElements.define('channel-label', ChannelLabel); + +/** + * SubmissionControls: Provides functionality to submit or discard a change. + */ +class SubmissionControls extends HTMLElement { + /** + * @param {function} submitCallback executed if the submit button is clicked + * @param {function} cancelCallback executed if the cancel button is clicked + */ + constructor(submitCallback, cancelCallback) { + super(); + + const padding = document.createElement('span'); + padding.setAttribute('id', 'submission-controls-padding'); + this.append(padding); + + this.submitButton_ = new SubmissionButton(submitCallback, + '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' + + 'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' + + '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' + + '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="black"/></svg>' + ); + this.cancelButton_ = new SubmissionButton(cancelCallback, + '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' + + 'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' + + '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' + + '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' + + '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' + + 'fill="black"/></svg>' + ); + this.append(this.submitButton_, this.cancelButton_); + } +} +window.customElements.define('submission-controls', SubmissionControls); + +/** + * SubmissionButton: Button with a custom look that can be clicked for + * a submission action. + */ +class SubmissionButton extends HTMLElement { + /** + * @param {function} clickCallback executed when the button is clicked + * @param {string} htmlString custom look for the button + */ + constructor(clickCallback, htmlString) { + super(); + + this.setAttribute('tabIndex', '0'); + this.innerHTML = htmlString; + + this.addEventListener('click', clickCallback); + } +} +window.customElements.define('submission-button', SubmissionButton); \ No newline at end of file
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js b/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js new file mode 100644 index 0000000..ba1120a2 --- /dev/null +++ b/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js
@@ -0,0 +1,73 @@ +// 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. + +var global = {argumentsReceived: false, params: null}; + +// DefaultColor is used by ColorPicker when selectedColor is not provided. +var DefaultColor = '#000000'; + +// DefaultColorPalette is used by ColorSuggestionPicker when the list of values +// is empty. +var DefaultColorPalette = [ + '#000000', '#404040', '#808080', '#c0c0c0', '#ffffff', '#980000', '#ff0000', + '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#4a86e8', '#0000ff', '#9900ff', + '#ff00ff' +]; + +/** + * @param {Event} event + */ +function handleMessage(event) { + initialize(JSON.parse(event.data)); + global.argumentsReceived = true; +} + +if (window.dialogArguments) { + initialize(dialogArguments); +} else { + window.addEventListener('message', handleMessage, false); + window.setTimeout(handleArgumentsTimeout, 1000); +} + +/** + * @param {!Object} args + */ +function initialize(args) { + global.params = args; + var main = $('main'); + main.innerHTML = ''; + var errorString; + if (global.params.shouldShowColorSuggestionPicker) { + main.classList.add('color-suggestion-picker-main'); + errorString = validateColorSuggestionPickerArguments(args); + } else { + main.classList.add('color-picker-main'); + errorString = validateColorPickerArguments(args); + } + if (errorString) { + main.textContent = 'Internal error: ' + errorString; + resizeWindow(main.offsetWidth, main.offsetHeight); + } else { + if (global.params.shouldShowColorSuggestionPicker) { + initializeColorSuggestionPicker(); + } else { + initializeColorPicker(); + } + } +} + +function handleArgumentsTimeout() { + if (global.argumentsReceived) + return; + // When an argument timeout happens, we do not have enough information to + // determine whether to show the color picker or color suggestion picker. + // In this case, we just choose to show the color suggestion picker. + var args = { + values: DefaultColorPalette, + otherColorLabel: 'Other...', + shouldShowColorSuggestionPicker: true, + selectedColor: DefaultColor, + }; + initialize(args); +} \ No newline at end of file
diff --git a/third_party/blink/renderer/core/html/resources/controls_refresh.css b/third_party/blink/renderer/core/html/resources/controls_refresh.css index b8d62d3..5b51afe 100644 --- a/third_party/blink/renderer/core/html/resources/controls_refresh.css +++ b/third_party/blink/renderer/core/html/resources/controls_refresh.css
@@ -9,17 +9,20 @@ */ input, +select, textarea { background-color: #ffffff; border-color: #cecece; } input:hover, +select:hover, textarea:hover { border-color: #9d9d9d; } input:disabled, +select:disabled, textarea:disabled { background-color: #ffffff; border-color: #c5c5c5;
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc index 255d4aa..9493b0f 100644 --- a/third_party/blink/renderer/core/layout/layout_view.cc +++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -96,6 +96,8 @@ : LayoutBlockFlow(document), frame_view_(document->View()), layout_state_(nullptr), + // TODO(pdr): This should be null if CompositeAfterPaintEnabled() is true. + compositor_(std::make_unique<PaintLayerCompositor>(*this)), layout_quote_head_(nullptr), layout_counter_count_(0), hit_test_count_(0), @@ -802,15 +804,13 @@ } PaintLayerCompositor* LayoutView::Compositor() { - if (!compositor_) - compositor_ = std::make_unique<PaintLayerCompositor>(*this); - + DCHECK(compositor_); return compositor_.get(); } -void LayoutView::SetIsInWindow(bool is_in_window) { - if (compositor_) - compositor_->SetIsInWindow(is_in_window); +void LayoutView::CleanUpCompositor() { + DCHECK(compositor_); + compositor_->CleanUp(); } IntervalArena* LayoutView::GetIntervalArena() {
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h index 1759734..ca4d4bb 100644 --- a/third_party/blink/renderer/core/layout/layout_view.h +++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -59,8 +59,13 @@ public: explicit LayoutView(Document*); ~LayoutView() override; + void WillBeDestroyed() override; + // Called when the Document is shutdown, to have the compositor clean up + // during frame detach, while pointers remain valid. + void CleanUpCompositor(); + // hitTest() will update layout, style and compositing first while // hitTestNoLifecycleUpdate() does not. bool HitTest(const HitTestLocation& location, HitTestResult&); @@ -185,9 +190,6 @@ page_logical_height_ = height; } - // Notification that this view moved into or out of a native window. - void SetIsInWindow(bool); - PaintLayerCompositor* Compositor(); bool UsesCompositing() const;
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h index d9898e61..0ddb011 100644 --- a/third_party/blink/renderer/core/page/chrome_client.h +++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -342,8 +342,13 @@ virtual void AttachRootLayer(scoped_refptr<cc::Layer>, LocalFrame* local_root) = 0; + // Set the CompositorAnimationTimeline for a local root. Should later be unset + // by a call to DetachCompositorAnimationTimeline(). virtual void AttachCompositorAnimationTimeline(CompositorAnimationTimeline*, LocalFrame* local_root) {} + // Removes the CompositorAnimationTimeline for a local root. The timeline + // would have previously been given to AttachCompositorAnimationTimeline() but + // it's valid to call this even if the timeline was never attached. virtual void DetachCompositorAnimationTimeline(CompositorAnimationTimeline*, LocalFrame* local_root) {}
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc index 5aadacba..138801a 100644 --- a/third_party/blink/renderer/core/page/chrome_client_impl.cc +++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -782,28 +782,27 @@ void ChromeClientImpl::AttachCompositorAnimationTimeline( CompositorAnimationTimeline* compositor_timeline, LocalFrame* local_frame) { - if (!Platform::Current()->IsThreadedAnimationEnabled()) - return; + DCHECK(Platform::Current()->IsThreadedAnimationEnabled()); WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(local_frame); - if (WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget()) { - widget->AnimationHost()->AddAnimationTimeline( - compositor_timeline->GetAnimationTimeline()); - } + WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget(); + // TODO(crbug.com/912193): This is called while a frame is attached so widget + // is never null, right? + CHECK(widget); + widget->AnimationHost()->AddAnimationTimeline( + compositor_timeline->GetAnimationTimeline()); } void ChromeClientImpl::DetachCompositorAnimationTimeline( CompositorAnimationTimeline* compositor_timeline, LocalFrame* local_frame) { - if (!Platform::Current()->IsThreadedAnimationEnabled()) - return; + DCHECK(Platform::Current()->IsThreadedAnimationEnabled()); WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(local_frame); - // This method can be called when the frame is being detached, after the - // widget is destroyed. - // TODO(dcheng): This should be called before the widget is gone... - if (WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget()) { - widget->AnimationHost()->RemoveAnimationTimeline( - compositor_timeline->GetAnimationTimeline()); - } + WebFrameWidgetBase* widget = web_frame->LocalRootFrameWidget(); + // TODO(crbug.com/912193): This should not be called after Document::Shutdown, + // so widget is never null, right? + CHECK(widget); + widget->AnimationHost()->RemoveAnimationTimeline( + compositor_timeline->GetAnimationTimeline()); } void ChromeClientImpl::EnterFullscreen(LocalFrame& frame,
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc index fe77631e..faf70524 100644 --- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc +++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" #include "base/optional.h" +#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/animation/document_timeline.h" #include "third_party/blink/renderer/core/animation/element_animations.h" @@ -67,12 +68,7 @@ namespace blink { PaintLayerCompositor::PaintLayerCompositor(LayoutView& layout_view) - : layout_view_(layout_view), - pending_update_type_(kCompositingUpdateNone), - has_accelerated_compositing_(true), - compositing_(false), - root_should_always_composite_dirty_(true), - root_layer_attachment_(kRootLayerUnattached) { + : layout_view_(layout_view) { UpdateAcceleratedCompositingSettings(); } @@ -80,6 +76,18 @@ DCHECK_EQ(root_layer_attachment_, kRootLayerUnattached); } +void PaintLayerCompositor::CleanUp() { + if (InCompositingMode()) + DetachRootLayer(); +} + +void PaintLayerCompositor::DidLayout() { + // FIXME: Technically we only need to do this when the LocalFrameView's + // isScrollable method would return a different value. + root_should_always_composite_dirty_ = true; + EnableCompositingModeIfNeeded(); +} + bool PaintLayerCompositor::InCompositingMode() const { // FIXME: This should assert that lifecycle is >= CompositingClean since // the last step of updateIfNeeded can set this bit to false. @@ -98,9 +106,9 @@ compositing_ = enable; if (compositing_) - EnsureRootLayer(); + AttachRootLayer(); else - DestroyRootLayer(); + DetachRootLayer(); // Schedule an update in the parent frame so the <iframe>'s layer in the owner // document matches the compositing state here. @@ -127,14 +135,19 @@ } bool PaintLayerCompositor::RootShouldAlwaysComposite() const { + // If compositing is disabled for the WebView, then nothing composites. if (!has_accelerated_compositing_) return false; - // Should not composite if not attached to a frame view. + // Local roots composite always, when compositing is enabled globally. + if (layout_view_.GetFrame()->IsLocalRoot()) + return true; + // Some non-local roots of embedded content do not have a LocalFrameView + // so they are not visible and should not get compositor layers. if (!layout_view_.GetFrameView()) return false; - return layout_view_.GetFrame()->IsLocalRoot() || - CompositingReasonFinder::RequiresCompositingForScrollableFrame( - layout_view_); + // Non-local root frames will composite only if needed for scrolling. + return CompositingReasonFinder::RequiresCompositingForScrollableFrame( + layout_view_); } void PaintLayerCompositor::UpdateAcceleratedCompositingSettings() { @@ -229,9 +242,9 @@ ScriptForbiddenScope forbid_script; - // FIXME: enableCompositingModeIfNeeded can trigger a + // FIXME: EnableCompositingModeIfNeeded() can trigger a // CompositingUpdateRebuildTree, which asserts that it's not - // InCompositingUpdate. + // InCompositingUpdate(). EnableCompositingModeIfNeeded(); #if DCHECK_IS_ON() @@ -304,6 +317,13 @@ #endif } +#if DCHECK_IS_ON() +void PaintLayerCompositor::AssertNoUnresolvedDirtyBits() { + DCHECK_EQ(pending_update_type_, kCompositingUpdateNone); + DCHECK(!root_should_always_composite_dirty_); +} +#endif + void PaintLayerCompositor::SetNeedsCompositingUpdate( CompositingUpdateType update_type) { DCHECK_NE(update_type, kCompositingUpdateNone); @@ -317,22 +337,6 @@ Lifecycle().EnsureStateAtMost(DocumentLifecycle::kLayoutClean); } -void PaintLayerCompositor::DidLayout() { - // FIXME: Technically we only need to do this when the LocalFrameView's - // isScrollable method would return a different value. - root_should_always_composite_dirty_ = true; - EnableCompositingModeIfNeeded(); -} - -#if DCHECK_IS_ON() - -void PaintLayerCompositor::AssertNoUnresolvedDirtyBits() { - DCHECK_EQ(pending_update_type_, kCompositingUpdateNone); - DCHECK(!root_should_always_composite_dirty_); -} - -#endif - GraphicsLayer* PaintLayerCompositor::OverlayFullscreenVideoGraphicsLayer() { LayoutVideo* video = FindFullscreenVideoLayoutObject(layout_view_.GetDocument()); @@ -461,6 +465,9 @@ return; } + if (IsMainFrame()) + GetPage()->GetVisualViewport().CreateLayerTree(); + PaintLayer* update_root = RootLayer(); Vector<PaintLayer*> layers_needing_paint_invalidation; @@ -654,7 +661,7 @@ PaintLayerCompositor* inner_compositor = FrameContentsCompositor( ToLayoutEmbeddedContent(layer->GetLayoutObject())); if (inner_compositor && inner_compositor->StaleInCompositingMode()) - inner_compositor->EnsureRootLayer(); + inner_compositor->AttachRootLayer(); } layer->ClearClipRects(kPaintingClipRects); @@ -771,25 +778,6 @@ return nullptr; } -void PaintLayerCompositor::SetIsInWindow(bool is_in_window) { - if (!StaleInCompositingMode()) - return; - - if (is_in_window) { - if (root_layer_attachment_ != kRootLayerUnattached) - return; - - AttachCompositorTimeline(); - AttachRootLayer(); - } else { - if (root_layer_attachment_ == kRootLayerUnattached) - return; - - DetachRootLayer(); - DetachCompositorTimeline(); - } -} - void PaintLayerCompositor::UpdatePotentialCompositingReasonsFromStyle( PaintLayer& layer) { auto reasons = CompositingReasonFinder::PotentialCompositingReasonsFromStyle( @@ -869,44 +857,35 @@ UpdateTrackingRasterInvalidationsRecursive(root_layer); } -void PaintLayerCompositor::EnsureRootLayer() { +void PaintLayerCompositor::AttachRootLayer() { if (root_layer_attachment_ != kRootLayerUnattached) return; - if (IsMainFrame()) - GetVisualViewport().CreateLayerTree(); - - AttachCompositorTimeline(); - AttachRootLayer(); -} - -void PaintLayerCompositor::DestroyRootLayer() { - DetachRootLayer(); -} - -void PaintLayerCompositor::AttachRootLayer() { // With CompositeAfterPaint, PaintArtifactCompositor is responsible for the // root layer. if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) return; + // Local roots are attached to the ChromeClient later. Otherwise, we can + // attach the layer into the existing compositor layer tree. if (layout_view_.GetFrame()->IsLocalRoot()) { root_layer_attachment_ = kRootLayerPendingAttachViaChromeClient; - } else { - HTMLFrameOwnerElement* owner_element = - layout_view_.GetDocument().LocalOwner(); - DCHECK(owner_element); - // The layer will get hooked up via - // CompositedLayerMapping::updateGraphicsLayerConfiguration() for the - // frame's layoutObject in the parent document. - owner_element->SetNeedsCompositingUpdate(); - if (owner_element->GetLayoutObject()) { - ToLayoutBoxModelObject(owner_element->GetLayoutObject()) - ->Layer() - ->SetNeedsCompositingInputsUpdate(); - } - root_layer_attachment_ = kRootLayerAttachedViaEnclosingFrame; + return; } + + HTMLFrameOwnerElement* owner_element = + layout_view_.GetDocument().LocalOwner(); + DCHECK(owner_element); + // The layer will get hooked up via + // CompositedLayerMapping::updateGraphicsLayerConfiguration() for the + // frame's layoutObject in the parent document. + owner_element->SetNeedsCompositingUpdate(); + if (owner_element->GetLayoutObject()) { + ToLayoutBoxModelObject(owner_element->GetLayoutObject()) + ->Layer() + ->SetNeedsCompositingInputsUpdate(); + } + root_layer_attachment_ = kRootLayerAttachedViaEnclosingFrame; } void PaintLayerCompositor::DetachRootLayer() { @@ -925,10 +904,8 @@ } case kRootLayerAttachedViaChromeClient: { LocalFrame& frame = layout_view_.GetFrameView()->GetFrame(); - Page* page = frame.GetPage(); - if (!page) - return; - page->GetChromeClient().AttachRootGraphicsLayer(nullptr, &frame); + ChromeClient& chrome_client = frame.GetPage()->GetChromeClient(); + chrome_client.AttachRootGraphicsLayer(nullptr, &frame); break; } case kRootLayerPendingAttachViaChromeClient: @@ -939,34 +916,6 @@ root_layer_attachment_ = kRootLayerUnattached; } -void PaintLayerCompositor::AttachCompositorTimeline() { - LocalFrame& frame = layout_view_.GetFrameView()->GetFrame(); - Page* page = frame.GetPage(); - if (!page || !frame.GetDocument()) - return; - - CompositorAnimationTimeline* compositor_timeline = - frame.GetDocument()->Timeline().CompositorTimeline(); - if (compositor_timeline) { - page->GetChromeClient().AttachCompositorAnimationTimeline( - compositor_timeline, &frame); - } -} - -void PaintLayerCompositor::DetachCompositorTimeline() { - LocalFrame& frame = layout_view_.GetFrameView()->GetFrame(); - Page* page = frame.GetPage(); - if (!page || !frame.GetDocument()) - return; - - CompositorAnimationTimeline* compositor_timeline = - frame.GetDocument()->Timeline().CompositorTimeline(); - if (compositor_timeline) { - page->GetChromeClient().DetachCompositorAnimationTimeline( - compositor_timeline, &frame); - } -} - ScrollingCoordinator* PaintLayerCompositor::GetScrollingCoordinator() const { if (Page* page = GetPage()) return page->GetScrollingCoordinator();
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h index bbb7d42..859d8885 100644 --- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h +++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h
@@ -76,6 +76,15 @@ explicit PaintLayerCompositor(LayoutView&); ~PaintLayerCompositor(); + // Called while the LocalFrame behind the LayoutView is being detached. + // Pointers in other objects should be cleaned up at this point, before + // pointers out of this object become invalid. + void CleanUp(); + + // Called after layout is performed on the LocalFrame holding the LayoutView, + // during the document lifecycle update. + void DidLayout(); + void UpdateIfNeededRecursive(DocumentLifecycle::LifecycleState target_state); // Return true if this LayoutView is in "compositing mode" (i.e. has one or @@ -97,14 +106,14 @@ bool RootShouldAlwaysComposite() const; // Copy the accelerated compositing related flags from Settings + // TODO(danakj): This setting shouldn't change after initialization, remove + // this public method. void UpdateAcceleratedCompositingSettings(); // Used to indicate that a compositing update will be needed for the next // frame that gets drawn. void SetNeedsCompositingUpdate(CompositingUpdateType); - void DidLayout(); - // Whether layer's compositedLayerMapping needs a GraphicsLayer to clip // z-order children of the given Layer. bool ClipsCompositingDescendants(const PaintLayer*) const; @@ -131,8 +140,6 @@ // The LayoutView's scroll layer. GraphicsLayer* ScrollLayer() const; - void SetIsInWindow(bool); - static PaintLayerCompositor* FrameContentsCompositor(LayoutEmbeddedContent&); // Return true if the layers changed. static bool AttachFrameContentLayersToIframeLayer(LayoutEmbeddedContent&); @@ -183,15 +190,9 @@ void UpdateIfNeeded(DocumentLifecycle::LifecycleState target_state, CompositingReasonsStats&); - void EnsureRootLayer(); - void DestroyRootLayer(); - void AttachRootLayer(); void DetachRootLayer(); - void AttachCompositorTimeline(); - void DetachCompositorTimeline(); - Page* GetPage() const; ScrollingCoordinator* GetScrollingCoordinator() const; @@ -214,19 +215,21 @@ LayoutView& layout_view_; - CompositingUpdateType pending_update_type_; + // After initialization, compositing updates must be done, so start dirty. + CompositingUpdateType pending_update_type_ = + kCompositingUpdateAfterCompositingInputChange; - bool has_accelerated_compositing_; - bool compositing_; + bool has_accelerated_compositing_ = true; + bool compositing_ = false; // The root layer doesn't composite if it's a non-scrollable frame. // So, after a layout we set this dirty bit to know that we need // to recompute whether the root layer should composite even if // none of its descendants composite. - // FIXME: Get rid of all the callers of setCompositingModeEnabled - // except the one in updateIfNeeded, then rename this to - // m_compositingDirty. - bool root_should_always_composite_dirty_; + // FIXME: Get rid of all the callers of SetCompositingModeEnabled() + // except the one in UpdateIfNeeded(), then rename this to + // compositing_dirty_. + bool root_should_always_composite_dirty_ = true; enum RootLayerAttachment { kRootLayerUnattached, @@ -234,7 +237,7 @@ kRootLayerAttachedViaChromeClient, kRootLayerAttachedViaEnclosingFrame }; - RootLayerAttachment root_layer_attachment_; + RootLayerAttachment root_layer_attachment_ = kRootLayerUnattached; CompositingInputsRoot compositing_inputs_root_;
diff --git a/third_party/blink/renderer/core/paint/theme_painter_default.cc b/third_party/blink/renderer/core/paint/theme_painter_default.cc index 24a6830..3468fe969 100644 --- a/third_party/blink/renderer/core/paint/theme_painter_default.cc +++ b/third_party/blink/renderer/core/paint/theme_painter_default.cc
@@ -331,8 +331,10 @@ extra_params.menu_list.arrow_size = arrow_size; } else { // TODO(tkent): This should be 7.0 to match scroll bar buttons. - float arrow_size = 6.0 * arrow_scale_factor; - // Put the 6px arrow at the center of paddingForArrow area. + float arrow_size = + (RuntimeEnabledFeatures::FormControlsRefreshEnabled() ? 12.0 : 6.0) * + arrow_scale_factor; + // Put the arrow at the center of paddingForArrow area. // |arrowX| is the left position for Aura theme engine. extra_params.menu_list.arrow_x = (style.Direction() == TextDirection::kRtl)
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc index 99fb937..b166bef0 100644 --- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc +++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -67,32 +67,152 @@ template <> DawnTextureFormat AsDawnEnum<DawnTextureFormat>( const WTF::String& webgpu_enum) { - if (webgpu_enum == "rgba8unorm") { - return DAWN_TEXTURE_FORMAT_RGBA8_UNORM; - } - if (webgpu_enum == "rg8unorm") { - return DAWN_TEXTURE_FORMAT_RG8_UNORM; - } + // Normal 8 bit formats if (webgpu_enum == "r8unorm") { return DAWN_TEXTURE_FORMAT_R8_UNORM; } - if (webgpu_enum == "rgba8uint") { - return DAWN_TEXTURE_FORMAT_RGBA8_UINT; - } - if (webgpu_enum == "r8g8uint") { - return DAWN_TEXTURE_FORMAT_RG8_UINT; + if (webgpu_enum == "r8snorm") { + return DAWN_TEXTURE_FORMAT_R8_SNORM; } if (webgpu_enum == "r8uint") { return DAWN_TEXTURE_FORMAT_R8_UINT; } + if (webgpu_enum == "r8sint") { + return DAWN_TEXTURE_FORMAT_R8_SINT; + } + + // Normal 16 bit formats + if (webgpu_enum == "r16unorm") { + return DAWN_TEXTURE_FORMAT_R16_UNORM; + } + if (webgpu_enum == "r16snorm") { + return DAWN_TEXTURE_FORMAT_R16_SNORM; + } + if (webgpu_enum == "r16uint") { + return DAWN_TEXTURE_FORMAT_R16_UINT; + } + if (webgpu_enum == "r16sint") { + return DAWN_TEXTURE_FORMAT_R16_SINT; + } + if (webgpu_enum == "r16float") { + return DAWN_TEXTURE_FORMAT_R16_FLOAT; + } + if (webgpu_enum == "rg8unorm") { + return DAWN_TEXTURE_FORMAT_RG8_UNORM; + } + if (webgpu_enum == "rg8snorm") { + return DAWN_TEXTURE_FORMAT_RG8_SNORM; + } + if (webgpu_enum == "rg8uint") { + return DAWN_TEXTURE_FORMAT_RG8_UINT; + } + if (webgpu_enum == "rg8sint") { + return DAWN_TEXTURE_FORMAT_RG8_SINT; + } + + // Normal 32 bit formats + if (webgpu_enum == "r32uint") { + return DAWN_TEXTURE_FORMAT_R32_UINT; + } + if (webgpu_enum == "r32sint") { + return DAWN_TEXTURE_FORMAT_R32_SINT; + } + if (webgpu_enum == "r32float") { + return DAWN_TEXTURE_FORMAT_R32_FLOAT; + } + if (webgpu_enum == "rg16unorm") { + return DAWN_TEXTURE_FORMAT_RG16_UNORM; + } + if (webgpu_enum == "rg16snorm") { + return DAWN_TEXTURE_FORMAT_RG16_SNORM; + } + if (webgpu_enum == "rg16uint") { + return DAWN_TEXTURE_FORMAT_RG16_UINT; + } + if (webgpu_enum == "rg16sint") { + return DAWN_TEXTURE_FORMAT_RG16_SINT; + } + if (webgpu_enum == "rg16float") { + return DAWN_TEXTURE_FORMAT_RG16_FLOAT; + } + if (webgpu_enum == "rgba8unorm") { + return DAWN_TEXTURE_FORMAT_RGBA8_UNORM; + } + if (webgpu_enum == "rgba8unorm-srgb") { + return DAWN_TEXTURE_FORMAT_RGBA8_UNORM_SRGB; + } + if (webgpu_enum == "rgba8snorm") { + return DAWN_TEXTURE_FORMAT_RGBA8_SNORM; + } + if (webgpu_enum == "rgba8uint") { + return DAWN_TEXTURE_FORMAT_RGBA8_UINT; + } + if (webgpu_enum == "rgba8sint") { + return DAWN_TEXTURE_FORMAT_RGBA8_SINT; + } if (webgpu_enum == "bgra8unorm") { return DAWN_TEXTURE_FORMAT_BGRA8_UNORM; } - if (webgpu_enum == "depth32float-stencil8") { + if (webgpu_enum == "bgra8unorm-srgb") { + return DAWN_TEXTURE_FORMAT_BGRA8_UNORM_SRGB; + } + + // Packed 32 bit formats + if (webgpu_enum == "rgb10a2unorm") { + return DAWN_TEXTURE_FORMAT_RGB10_A2_UNORM; + } + if (webgpu_enum == "rg11b10float") { + return DAWN_TEXTURE_FORMAT_RG11_B10_FLOAT; + } + + // Normal 64 bit formats + if (webgpu_enum == "rg32uint") { + return DAWN_TEXTURE_FORMAT_RG32_UINT; + } + if (webgpu_enum == "rg32sint") { + return DAWN_TEXTURE_FORMAT_RG32_SINT; + } + if (webgpu_enum == "rg32float") { + return DAWN_TEXTURE_FORMAT_RG32_FLOAT; + } + if (webgpu_enum == "rgba16unorm") { + return DAWN_TEXTURE_FORMAT_RGBA16_UNORM; + } + if (webgpu_enum == "rgba16snorm") { + return DAWN_TEXTURE_FORMAT_RGBA16_SNORM; + } + if (webgpu_enum == "rgba16uint") { + return DAWN_TEXTURE_FORMAT_RGBA16_UINT; + } + if (webgpu_enum == "rgba16sint") { + return DAWN_TEXTURE_FORMAT_RGBA16_SINT; + } + if (webgpu_enum == "rgba16float") { + return DAWN_TEXTURE_FORMAT_RGBA16_FLOAT; + } + + // Normal 128 bit formats + if (webgpu_enum == "rgba32uint") { + return DAWN_TEXTURE_FORMAT_RGBA32_UINT; + } + if (webgpu_enum == "rgba32sint") { + return DAWN_TEXTURE_FORMAT_RGBA32_SINT; + } + if (webgpu_enum == "rgba32float") { + return DAWN_TEXTURE_FORMAT_RGBA32_FLOAT; + } + + // Depth / Stencil formats + if (webgpu_enum == "depth32float") { + return DAWN_TEXTURE_FORMAT_DEPTH32_FLOAT; + } + if (webgpu_enum == "depth24plus") { + return DAWN_TEXTURE_FORMAT_DEPTH24_PLUS; + } + if (webgpu_enum == "depth24plus-stencil8") { return DAWN_TEXTURE_FORMAT_DEPTH24_PLUS_STENCIL8; } - // TODO(crbug.com/dawn/128): Implement the remaining texture formats. - NOTREACHED(); + return DAWN_TEXTURE_FORMAT_FORCE32; }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc index 07f6100f..753aabd 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc
@@ -61,8 +61,8 @@ float height, float minDepth, float maxDepth) { - // TODO(crbug.com/dawn/53): Implement setViewport in Dawn. - NOTIMPLEMENTED(); + GetProcs().renderPassEncoderSetViewport(GetHandle(), x, y, width, height, + minDepth, maxDepth); } void GPURenderPassEncoder::setScissorRect(uint32_t x,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture_descriptor.idl b/third_party/blink/renderer/modules/webgpu/gpu_texture_descriptor.idl index 656d6d5..241cdd6 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_texture_descriptor.idl +++ b/third_party/blink/renderer/modules/webgpu/gpu_texture_descriptor.idl
@@ -23,7 +23,6 @@ enum GPUTextureFormat { /* Normal 8 bit formats */ "r8unorm", - "r8unorm-srgb", "r8snorm", "r8uint", "r8sint", @@ -34,12 +33,9 @@ "r16sint", "r16float", "rg8unorm", - "rg8unorm-srgb", "rg8snorm", "rg8uint", "rg8sint", - /* Packed 16 bit formats */ - "b5g6r5unorm", /* Normal 32 bit formats */ "r32uint", "r32sint", @@ -74,5 +70,6 @@ "rgba32float", /* Depth / Stencil formats */ "depth32float", - "depth32float-stencil8", + "depth24plus", + "depth24plus-stencil8" };
diff --git a/third_party/blink/renderer/platform/geometry/length.h b/third_party/blink/renderer/platform/geometry/length.h index f44706997..f35afdd 100644 --- a/third_party/blink/renderer/platform/geometry/length.h +++ b/third_party/blink/renderer/platform/geometry/length.h
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "third_party/blink/renderer/platform/wtf/math_extras.h" namespace blink { @@ -86,7 +87,7 @@ Length(double v, Length::Type t, bool q = false) : quirk_(q), type_(t), is_float_(true) { - float_value_ = static_cast<float>(v); + float_value_ = clampTo<float>(v); } explicit Length(scoped_refptr<CalculationValue>);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index a185a4bc..29988e6 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -882,7 +882,6 @@ const Settings& settings) { DCHECK(NeedsUpdate()); DCHECK(root_layer_); - // The tree will be null after detaching and this update can be ignored. // See: WebViewImpl::detachPaintArtifactCompositor(). cc::LayerTreeHost* host = root_layer_->layer_tree_host();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 98d0690..5fd989ea 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2384,7 +2384,6 @@ crbug.com/490511 external/wpt/html/rendering/non-replaced-elements/the-hr-element-0/color.html [ Failure ] crbug.com/490511 external/wpt/html/rendering/non-replaced-elements/the-hr-element-0/width.html [ Failure ] crbug.com/692560 external/wpt/html/semantics/document-metadata/styling/LinkStyle.html [ Failure Pass ] -crbug.com/627706 external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html [ Skip ] crbug.com/860211 [ Mac ] external/wpt/editing/run/delete.html [ Failure ] @@ -6301,10 +6300,6 @@ crbug.com/985232 [ Win7 ] external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html [ Pass Failure ] crbug.com/985232 [ Win7 ] virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports-credentials.sub.html [ Pass Failure ] -# TODO (michaelludwig) - Remove after Skia rebaseline -crbug.com/985769 compositing/reflections/nested-reflection-transformed2.html [ Pass Failure ] -crbug.com/985769 virtual/disable-blink-gen-property-trees/compositing/reflections/nested-reflection-transformed2.html [ Pass Failure ] - # Sheriff 2019-07-19 crbug.com/985811 [ Win7 ] http/tests/devtools/elements/edit/edit-dom-actions-2.js [ Pass Timeout ] crbug.com/985869 [ Linux Win7 ] http/tests/security/contentSecurityPolicy/object-src-no-url-allowed.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html b/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html index b2bcffd..7ff11af1 100644 --- a/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html +++ b/third_party/blink/web_tests/animations/interpolation/perspective-origin-interpolation.html
@@ -97,5 +97,14 @@ {at: 1, is: '100% 150%'}, {at: 1.5, is: '150% 200%'} ]); + +// Regression test for https://crbug.com/984700 +assertInterpolation({ + property: 'perspective-origin', + from: '20% 25%', + to: '40% 50%', +}, [ + {at: 3.40282e+38, is: '3.35544e+07px 3.35544e+07px'} +]); </script> </body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json index 6be5f18..045f753 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -191664,6 +191664,12 @@ {} ] ], + "animation-worklet/worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html": [ + [ + "animation-worklet/worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html", + {} + ] + ], "animation-worklet/worklet-animation-pause.https.html": [ [ "animation-worklet/worklet-animation-pause.https.html", @@ -198182,6 +198188,12 @@ {} ] ], + "css/css-align/parsing/align-content-computed.html": [ + [ + "css/css-align/parsing/align-content-computed.html", + {} + ] + ], "css/css-align/parsing/align-content-invalid.html": [ [ "css/css-align/parsing/align-content-invalid.html", @@ -198194,6 +198206,12 @@ {} ] ], + "css/css-align/parsing/align-items-computed.html": [ + [ + "css/css-align/parsing/align-items-computed.html", + {} + ] + ], "css/css-align/parsing/align-items-invalid.html": [ [ "css/css-align/parsing/align-items-invalid.html", @@ -198206,6 +198224,12 @@ {} ] ], + "css/css-align/parsing/align-self-computed.html": [ + [ + "css/css-align/parsing/align-self-computed.html", + {} + ] + ], "css/css-align/parsing/align-self-invalid.html": [ [ "css/css-align/parsing/align-self-invalid.html", @@ -198218,6 +198242,12 @@ {} ] ], + "css/css-align/parsing/justify-content-computed.html": [ + [ + "css/css-align/parsing/justify-content-computed.html", + {} + ] + ], "css/css-align/parsing/justify-content-invalid.html": [ [ "css/css-align/parsing/justify-content-invalid.html", @@ -198230,6 +198260,12 @@ {} ] ], + "css/css-align/parsing/justify-items-computed.html": [ + [ + "css/css-align/parsing/justify-items-computed.html", + {} + ] + ], "css/css-align/parsing/justify-items-invalid.html": [ [ "css/css-align/parsing/justify-items-invalid.html", @@ -198242,6 +198278,12 @@ {} ] ], + "css/css-align/parsing/justify-self-computed.html": [ + [ + "css/css-align/parsing/justify-self-computed.html", + {} + ] + ], "css/css-align/parsing/justify-self-invalid.html": [ [ "css/css-align/parsing/justify-self-invalid.html", @@ -298536,6 +298578,18 @@ {} ] ], + "webxr/xrDevice_requestSession_optionalFeatures.https.html": [ + [ + "webxr/xrDevice_requestSession_optionalFeatures.https.html", + {} + ] + ], + "webxr/xrDevice_requestSession_requiredFeatures_unknown.https.html": [ + [ + "webxr/xrDevice_requestSession_requiredFeatures_unknown.https.html", + {} + ] + ], "webxr/xrDevice_supportsSession_immersive.https.html": [ [ "webxr/xrDevice_supportsSession_immersive.https.html", @@ -298680,6 +298734,12 @@ {} ] ], + "webxr/xrSession_requestReferenceSpace_features.https.html": [ + [ + "webxr/xrSession_requestReferenceSpace_features.https.html", + {} + ] + ], "webxr/xrSession_viewer_referenceSpace.https.html": [ [ "webxr/xrSession_viewer_referenceSpace.https.html", @@ -318070,7 +318130,7 @@ "testharness" ], "animation-worklet/common.js": [ - "3e542e02efceb5073a08a243855b3ac309b2e3b6", + "ceb430b718b8438c933071b1dd24240ab13bf562", "support" ], "animation-worklet/current-time.https.html": [ @@ -318141,6 +318201,10 @@ "1a8afc1e89278d06572e337965a6d1ff1572d0d9", "testharness" ], + "animation-worklet/worklet-animation-get-computed-timing-progress-on-worklet-thread.https.html": [ + "a66a4b9156cd9ee0283928feaf89a782a058998e", + "testharness" + ], "animation-worklet/worklet-animation-get-timing-on-worklet-thread-ref.html": [ "aad07d5f6f26692dc47ce0219a78d3c1a50c608a", "support" @@ -340309,6 +340373,10 @@ "7e2fa130be24214fa8c21f58d0f88a744beb6b79", "testharness" ], + "css/css-align/parsing/align-content-computed.html": [ + "8b5465c6b275118ec067cb6ef7b3026aca825e65", + "testharness" + ], "css/css-align/parsing/align-content-invalid.html": [ "ae244389bf1b54f37a9fd6a65111d5e8f3c703fc", "testharness" @@ -340317,6 +340385,10 @@ "46c4724516855f169fba15650e89d56e18d884c9", "testharness" ], + "css/css-align/parsing/align-items-computed.html": [ + "23d4c2fd0d1193181b2aea7b870f0b194dbcdcfd", + "testharness" + ], "css/css-align/parsing/align-items-invalid.html": [ "8e329d50e5c34a8fba0eb122be8686e1dfafed31", "testharness" @@ -340325,6 +340397,10 @@ "e88845b962ac2df802346b8d57c4b17147d79a81", "testharness" ], + "css/css-align/parsing/align-self-computed.html": [ + "e61a88a72e8fe9bb304758ea8a47e29263888a0b", + "testharness" + ], "css/css-align/parsing/align-self-invalid.html": [ "27b025002655d859c290a4e41d46289680a7a810", "testharness" @@ -340333,28 +340409,40 @@ "25ea093b20997419f4d560d47cb9300d136d090e", "testharness" ], + "css/css-align/parsing/justify-content-computed.html": [ + "b04d2db1429c385d57b9d6b232282d7924d6589b", + "testharness" + ], "css/css-align/parsing/justify-content-invalid.html": [ - "971f94bcf8be5ad4e9b4978dc3bc4211e9804881", + "47f3a59a7d7f9723c50d338b04a7c54a0b2f4dc3", "testharness" ], "css/css-align/parsing/justify-content-valid.html": [ - "d476a268addc672fdde32465eb2570315becf0cf", + "4372f6fdc31af0d41e0016aaf250450f8df0a407", + "testharness" + ], + "css/css-align/parsing/justify-items-computed.html": [ + "b93f2a381b110467e89767e0b1c643e245e4e2b0", "testharness" ], "css/css-align/parsing/justify-items-invalid.html": [ - "18da059376095c00c791445268ccba53066b749e", + "87d03cb3d124de868d430b3a10935c99882b2f67", "testharness" ], "css/css-align/parsing/justify-items-valid.html": [ - "36584f86feb4533b712c418f6cdc20f761d4f172", + "51496554cb0214d85c2561fc4b3a7d98340709dc", + "testharness" + ], + "css/css-align/parsing/justify-self-computed.html": [ + "f560d23d7b1df51de41e9339437d391248f931db", "testharness" ], "css/css-align/parsing/justify-self-invalid.html": [ - "846cdd8a00f028b2b04707b4325bfb10f0a5239a", + "7823c0aa586b1d9644973c92ca89fb725262518c", "testharness" ], "css/css-align/parsing/justify-self-valid.html": [ - "1ee23af200f6300bc3a86f0a1b843c875c8c92a9", + "4cca080995859320d7a89943caa8eada6440f46c", "testharness" ], "css/css-align/parsing/place-content-computed.html": [ @@ -368682,7 +368770,7 @@ "testharness" ], "css/css-properties-values-api/register-property-syntax-parsing.html": [ - "fe2a63a57a168c8e791c6cec9d9d1843ca2c1216", + "6d7dbc63a1d38f108a3a10e99b09cddd84b135f7", "testharness" ], "css/css-properties-values-api/register-property.html": [ @@ -442422,7 +442510,7 @@ "testharness" ], "mediasession/setactionhandler.html": [ - "06d145b37d1603680ef61de4b2953f5bfc4ed650", + "6e1bb26c67a828ae847734070024b404138e7932", "testharness" ], "mimesniff/META.yml": [ @@ -453030,11 +453118,11 @@ "manual" ], "payment-request/rejects_if_not_active.https-expected.txt": [ - "1fa19f3e96160290eca310571cce99cfbc513395", + "b9917d668a6bdfa0ce4f881a53d0177499916c11", "support" ], "payment-request/rejects_if_not_active.https.html": [ - "4ba760ac987746ad9b561377a6b1e4185f7e7331", + "1c666aea435758cd7f79172aaa691f43f7907a47", "testharness" ], "payment-request/resources/page1.html": [ @@ -479962,7 +480050,7 @@ "testharness" ], "web-animations/timing-model/timelines/document-timelines.html": [ - "be8e3cca1c77e34f4a52e5d4e51c423161b7886f", + "4023bba55612b689d98a9773c9c6fafe11408e2b", "testharness" ], "web-animations/timing-model/timelines/timelines.html": [ @@ -488350,7 +488438,7 @@ "support" ], "webxr/resources/webxr_util.js": [ - "3a6a6c96fe0c419c1ff7ed7c5ab347cbc6978c13", + "baf1d66b00daa98c07b9bfc91436018d12eef097", "support" ], "webxr/webGLCanvasContext_create_xrcompatible.https.html": [ @@ -488378,7 +488466,7 @@ "testharness" ], "webxr/xrDevice_requestSession_immersive.https.html": [ - "a397d1a89f6e0d3ac7784a39ac8612350aa69dcd", + "be0bb32995b86ee576c1e55414d7f7d81a1e0fc1", "testharness" ], "webxr/xrDevice_requestSession_immersive_no_gesture.https.html": [ @@ -488397,6 +488485,14 @@ "59950592558513f2530aaedaa86f89c955166863", "testharness" ], + "webxr/xrDevice_requestSession_optionalFeatures.https.html": [ + "c274f4ff7138df2bdc98fbc173db73d29321afc2", + "testharness" + ], + "webxr/xrDevice_requestSession_requiredFeatures_unknown.https.html": [ + "02b26c05cb5dc0e5b721e472632fae3aa83b6275", + "testharness" + ], "webxr/xrDevice_supportsSession_immersive.https.html": [ "fd0827a526ef01299eeccd94f93f131e4014cd7b", "testharness" @@ -488494,7 +488590,11 @@ "testharness" ], "webxr/xrSession_requestReferenceSpace.https.html": [ - "c90d6ce5a545563664ed34d946db2bca48edca40", + "51aa8885f5e853ca4bf225134dee120b71d28011", + "testharness" + ], + "webxr/xrSession_requestReferenceSpace_features.https.html": [ + "1bd516ee12e1d3ba24fd13ff04974c72e284b38a", "testharness" ], "webxr/xrSession_viewer_referenceSpace.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed.html new file mode 100644 index 0000000..8b5465c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().alignContent</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-content"> +<meta name="assert" content="align-content computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("align-content", "normal"); + +test_computed_value("align-content", "baseline"); +test_computed_value("align-content", "last baseline"); + +test_computed_value("align-content", "space-between"); +test_computed_value("align-content", "space-around"); +test_computed_value("align-content", "space-evenly"); +test_computed_value("align-content", "stretch"); + +test_computed_value("align-content", "center"); +test_computed_value("align-content", "start"); +test_computed_value("align-content", "end"); +test_computed_value("align-content", "flex-start"); +test_computed_value("align-content", "flex-end"); +test_computed_value("align-content", "unsafe end"); +test_computed_value("align-content", "safe flex-start"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed.html new file mode 100644 index 0000000..23d4c2f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().alignItems</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-items"> +<meta name="assert" content="align-items computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("align-items", "normal"); +test_computed_value("align-items", "stretch"); + +test_computed_value("align-items", "baseline"); +test_computed_value("align-items", "last baseline"); + +test_computed_value("align-items", "center"); +test_computed_value("align-items", "start"); +test_computed_value("align-items", "end"); +test_computed_value("align-items", "self-start"); +test_computed_value("align-items", "self-end"); +test_computed_value("align-items", "flex-start"); +test_computed_value("align-items", "flex-end"); +test_computed_value("align-items", "unsafe center"); +test_computed_value("align-items", "safe self-end"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed.html new file mode 100644 index 0000000..e61a88a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().alignSelf</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self"> +<meta name="assert" content="align-self computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("align-self", "auto"); +test_computed_value("align-self", "normal"); +test_computed_value("align-self", "stretch"); + +test_computed_value("align-self", "baseline"); +test_computed_value("align-self", "last baseline"); + +test_computed_value("align-self", "center"); +test_computed_value("align-self", "start"); +test_computed_value("align-self", "end"); +test_computed_value("align-self", "self-start"); +test_computed_value("align-self", "self-end"); +test_computed_value("align-self", "flex-start"); +test_computed_value("align-self", "flex-end"); +test_computed_value("align-self", "unsafe center"); +test_computed_value("align-self", "safe self-end"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-computed.html new file mode 100644 index 0000000..b04d2db --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-computed.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().justifyContent</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content"> +<meta name="assert" content="justify-content computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("justify-content", "normal"); + +test_computed_value("justify-content", "space-between"); +test_computed_value("justify-content", "space-around"); +test_computed_value("justify-content", "space-evenly"); +test_computed_value("justify-content", "stretch"); + +test_computed_value("justify-content", "center"); +test_computed_value("justify-content", "start"); +test_computed_value("justify-content", "end"); +test_computed_value("justify-content", "flex-start"); +test_computed_value("justify-content", "flex-end"); +test_computed_value("justify-content", "unsafe end"); +test_computed_value("justify-content", "safe flex-start"); +test_computed_value("justify-content", "left"); +test_computed_value("justify-content", "unsafe right"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-invalid.html index 971f94b..47f3a59 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-invalid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-content with invalid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-content"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content"> <meta name="assert" content="justify-content supports only the grammar 'normal | <content-distribution> | <overflow-position>? [ <content-position> | left | right ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-valid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-valid.html index d476a268..4372f6f 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-content-valid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-content with valid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-content"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-content"> <meta name="assert" content="justify-content supports the full grammar 'normal | <content-distribution> | <overflow-position>? [ <content-position> | left | right ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed.html new file mode 100644 index 0000000..b93f2a3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().justifyItems</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items"> +<meta name="assert" content="justify-items computed value is as specified."> +<meta name="assert" content="justify-items legacy depends on inherited value."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="container"> + <div id="target"></div> +</div> +<script> +test_computed_value("justify-items", "normal"); +test_computed_value("justify-items", "stretch"); + +test_computed_value("justify-items", "baseline"); +test_computed_value("justify-items", "last baseline"); + +test_computed_value("justify-items", "center"); +test_computed_value("justify-items", "start"); +test_computed_value("justify-items", "end"); +test_computed_value("justify-items", "self-start"); +test_computed_value("justify-items", "self-end"); +test_computed_value("justify-items", "flex-start"); +test_computed_value("justify-items", "flex-end"); +test_computed_value("justify-items", "unsafe center"); +test_computed_value("justify-items", "safe self-end"); +test_computed_value("justify-items", "right"); +test_computed_value("justify-items", "safe left"); + +test_computed_value("justify-items", "legacy", "normal"); +test_computed_value("justify-items", "legacy left"); +test_computed_value("justify-items", "legacy right"); +test_computed_value("justify-items", "legacy center"); + +test(() => { + const container = document.getElementById('container'); + const target = document.getElementById('target'); + target.style.justifyItems = 'legacy'; + container.style.justifyItems = 'legacy left'; + assert_equals(getComputedStyle(target).justifyItems, 'legacy left'); + container.style.justifyItems = 'legacy right'; + assert_equals(getComputedStyle(target).justifyItems, 'legacy right'); + container.style.justifyItems = 'legacy center'; + assert_equals(getComputedStyle(target).justifyItems, 'legacy center'); + container.style.justifyItems = 'flex-end'; + assert_equals(getComputedStyle(target).justifyItems, 'normal'); +}, 'justify-items legacy depends on inherited value'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-invalid.html index 18da059..87d03cb 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-invalid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-items with invalid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-items"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items"> <meta name="assert" content="justify-items supports only the grammar 'normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ] | legacy | legacy && [ left | right | center ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid.html index 36584f86..51496554 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-items with valid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-items"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-items"> <meta name="assert" content="justify-items supports the full grammar 'normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ] | legacy | legacy && [ left | right | center ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed.html new file mode 100644 index 0000000..f560d23d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().justifySelf</title> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self"> +<meta name="assert" content="justify-self computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("justify-self", "auto"); +test_computed_value("justify-self", "normal"); +test_computed_value("justify-self", "stretch"); + +test_computed_value("justify-self", "baseline"); +test_computed_value("justify-self", "last baseline"); + +test_computed_value("justify-self", "center"); +test_computed_value("justify-self", "start"); +test_computed_value("justify-self", "end"); +test_computed_value("justify-self", "self-start"); +test_computed_value("justify-self", "self-end"); +test_computed_value("justify-self", "flex-start"); +test_computed_value("justify-self", "flex-end"); +test_computed_value("justify-self", "unsafe center"); +test_computed_value("justify-self", "safe self-end"); +test_computed_value("justify-self", "left"); +test_computed_value("justify-self", "unsafe right"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-invalid.html index 846cdd8a..7823c0a 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-invalid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-self with invalid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-self"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self"> <meta name="assert" content="justify-self supports only the grammar 'auto | normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid.html index 1ee23af..4cca080 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid.html
@@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <title>CSS Box Alignment Level 3: parsing justify-self with valid values</title> -<link rel="help" href="https://drafts.csswg.org/css-justify-3/#propdef-justify-self"> +<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self"> <meta name="assert" content="justify-self supports the full grammar 'auto | normal | stretch | <baseline-position> | <overflow-position>? [ <self-position> | left | right ]'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-alpha.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-alpha.https.html index 53d869d0..7f1e31b 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-alpha.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-alpha.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="background-image-alpha-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-multiple.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-multiple.https.html index 79ff883..51c928d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-multiple.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-multiple.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="background-image-multiple-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-tiled.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-tiled.https.html index 8498c82a..11647c92 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-tiled.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/background-image-tiled.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="background-image-tiled-ref.html"> <style> div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-001.https.html index 601d418..eb7f671 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-001.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-background-image-001-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-002.https.html index 1d57073..e636aa7 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-002.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-background-image-002-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html index 8e28b54..51373a52 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-background-image-tiled-001-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-002.https.html index 9248e38..d55761b 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-002.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-background-image-tiled-002-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-003.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-003.https.html index 5b6b6c7..6d5bdd2 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-003.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-background-image-tiled-003.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-background-image-tiled-003-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-001.https.html index 3fb4643c..ff7f56e 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-001.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-border-image-001-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-002.https.html index 26e24bb..d6e47f0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-002.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-border-image-002-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-003.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-003.https.html index a26f2b7..36af043c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-003.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-003.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-border-image-003-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-004.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-004.https.html index 60db7ff..0f483cd5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-004.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-border-image-004.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-border-image-004-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-with-float-size.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-with-float-size.https.html index 65477e4..8cd90c8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-with-float-size.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/geometry-with-float-size.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="geometry-with-float-size-ref.html"> -<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/#dom-css-paintworklet"> <style> .container { width: 100.5px;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html index d160a86..6dc57ab 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html
@@ -1,6 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> -<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/#dom-css-paintworklet"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="canvas-transform-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/device-pixel-ratio.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/device-pixel-ratio.https.html index 46a9aa2..f9516e5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/device-pixel-ratio.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/hidpi/device-pixel-ratio.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="device-pixel-ratio-ref.html"> <style> html, body { margin: 0; padding: 0; }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/idlharness.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/idlharness.html index c9675ab..aad04a17 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/idlharness.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/idlharness.html
@@ -1,6 +1,6 @@ <!doctype html> <title>CSS Painting API IDL tests</title> -<link rel="help" href="https://drafts.css-houdini.org/css-paint-api-1/"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/resources/WebIDLParser.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-constructor-error.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-constructor-error.https.html index 439ff8b..8facde4 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-constructor-error.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-constructor-error.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="invalid-image-constructor-error-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-paint-error.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-paint-error.https.html index 2806ad92..17c283e 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-paint-error.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-paint-error.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="invalid-image-paint-error-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html index 4347a00..20a61eff 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="invalid-image-pending-script-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/overdraw.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/overdraw.https.html index 5be26f7e..8d3dc72 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/overdraw.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/overdraw.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class=reftest-wait> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="overdraw-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-arguments.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-arguments.https.html index abfb2a68..615027c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-arguments.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-arguments.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-function-arguments.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-function-arguments.https.html index d87b0b8..3a1e579 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-function-arguments.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint-function-arguments.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint-function-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-composite.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-composite.https.html index 80e94e4..edf8a766 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-composite.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-composite.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-composite-ref.html"> <style> div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-filter.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-filter.https.html index d0c45395..9d598c7 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-filter.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-filter.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-filter-ref.html"> <style> div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-gradient.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-gradient.https.html index 892a791..1887f52 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-gradient.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-gradient.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-gradient-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-image.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-image.https.html index 6fce4b6..71074df 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-image.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-image.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-image-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-paths.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-paths.https.html index 091f548c..0e04168f 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-paths.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-paths.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-paths-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-rects.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-rects.https.html index 2494272..9279aa2 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-rects.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-rects.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-rects-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-shadows.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-shadows.https.html index 98dcfbc..b641dab8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-shadows.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-shadows.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-shadows-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-transform.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-transform.https.html index c91b500..d052b8c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-transform.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/paint2d-transform.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="paint2d-transform-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-001.https.html index 4356ce6..486f379d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-001.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-002.https.html index b1b50e0..5c7b3aca0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-002.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-003.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-003.https.html index 3b1b0014..81904ee6 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-003.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-003.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-004.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-004.https.html index 138a790..9bf264b 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-004.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-004.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-005.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-005.https.html index 0c5e539..24769f47 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-005.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-005.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html index e99ab05..a6fcc16c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-006.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-007.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-007.https.html index 53f245b..462cfb6 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-007.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-007.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-008.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-008.https.html index e6b863c64..d5c18b0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-008.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-008.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html index b23191a..7cfdf91 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-010.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-010.https.html index e98b4316..142ca0c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-010.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-010.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html index b9d54bf..23eb9d3 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-012.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-012.https.html index 14eebeb..fa2c6b5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-012.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-012.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-013.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-013.https.html index 37e3eb435..adc1c03 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-013.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-013.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-014.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-014.https.html index 690e488..d041ae3 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-014.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-014.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-015.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-015.https.html index fba7671..8ebe3e7c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-015.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-015.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-016.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-016.https.html index 31cca45f..ccd0595 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-016.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-016.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-017.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-017.https.html index 0d14fe74..476e29f 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-017.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-017.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html index 541332a..e6a31cb 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-018-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-019.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-019.https.html index 707a0c6e..309ec800 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-019.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-019.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-020.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-020.https.html index fe8dbad2..7e00b641 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-020.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-020.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-021.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-021.https.html index 0c3a596..8fa043a8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-021.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-021.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-022.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-022.https.html index 50aaa6b..2ff27d7 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-022.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parse-input-arguments-022.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html index c6e9302..5aaed625 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <angle> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-002.https.html index cfa9a61..80b2c933 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-002.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <color> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-003.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-003.https.html index 314d0c0..683ed40 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-003.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-003.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <integer> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html index 430326a..9602e902 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-004.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <length-percentage> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-005.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-005.https.html index 807a050..35d6ccbf 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-005.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-005.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <length> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-006.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-006.https.html index 45859a51..a8cfdead 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-006.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-006.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <number> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html index b3f4f1f..77b80b5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <percentage> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-008.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-008.https.html index 45eb909..1ec1666 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-008.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-008.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <resolution> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-009.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-009.https.html index e458812..e7a5e37 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-009.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-009.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated <time> values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html index d4d1b03..ee3a0f1 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-interpolation-010.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Interpolated list values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-001.https.html index 26da7e2..133b923 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-001.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Registering a property causes invalidation for initial value</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-002.https.html index 0aab051..aadbfec 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-invalidation-002.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Registering a property causes invalidation for applied value</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-stylemap.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-stylemap.https.html index 2305afb..4a6b4db5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-stylemap.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-stylemap.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Test styleMap functions</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-001.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-001.https.html index f915dacb..970e1f3 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-001.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-001.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Initial values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-002.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-002.https.html index 7d62b0b..6019151 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-002.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-002.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Inherited values reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-003.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-003.https.html index b22f18d..b9c3bf3 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-003.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-003.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of *-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-004.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-004.https.html index d67603f..e04be69 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-004.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-004.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <angle>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-005.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-005.https.html index 71cda9a..bfecd0c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-005.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-005.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <color>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-006.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-006.https.html index aa03edf2..7176b67d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-006.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-006.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <custom-ident>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-007.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-007.https.html index 61256cd..309fe96 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-007.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-007.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <image>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-008.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-008.https.html index 06a18bd..db72408d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-008.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-008.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <integer>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-009.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-009.https.html index 2ce2a0d5..a2c2e9a 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-009.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-009.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <length-percentage>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-010.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-010.https.html index 53aef81..332a22a 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-010.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-010.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <length>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-011.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-011.https.html index d5dc351f..a596a5d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-011.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-011.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <number>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-012.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-012.https.html index 827877a..80c1e6e 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-012.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-012.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <percentage>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-013.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-013.https.html index e37cd556..7af0392 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-013.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-013.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <resolution>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-014.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-014.https.html index f42160f..0ca1eca 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-014.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-014.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <time>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-015.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-015.https.html index 5b00b53..d7ba26b8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-015.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-015.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of <url>-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-016.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-016.https.html index 1ec2e1b5..609f00f6 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-016.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-016.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of ident-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-017.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-017.https.html index 1ec2e1b5..609f00f6 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-017.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-017.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of ident-properties reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-018.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-018.https.html index 6be2cb8a..1f9876b6 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-018.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/registered-property-value-018.https.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>Values of lists reach worklet</title> -<link rel="help" href="https://www.w3.org/TR/css-paint-api-1/#examples"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="parse-input-arguments-ref.html"> <script src="/common/reftest-wait.js"></script> <script src="/common/worklet-reftest.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-background-image.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-background-image.https.html index 13204cf7..8e5f2f8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-background-image.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-background-image.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="style-background-image-ref.html"> <style> .container {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-before-pseudo.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-before-pseudo.https.html index f7e2a164..017b23a 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-before-pseudo.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-before-pseudo.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="style-before-pseudo-ref.html"> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html index 02b1145..d3c31a05 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/style-first-letter-pseudo.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="style-first-letter-pseudo-ref.html"> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-after-load.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-after-load.https.html index 44612f98..c1bf8e8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-after-load.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-after-load.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="valid-image-after-load-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-before-load.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-before-load.https.html index 7738d9dd..b58dfa1 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-before-load.https.html +++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/valid-image-before-load.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> +<link rel="help" href="https://drafts.css-houdini.org/css-paint-api/"> <link rel="match" href="valid-image-before-load-ref.html"> <style> #output {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src-expected.txt new file mode 100644 index 0000000..4b8f9770 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src-expected.txt
@@ -0,0 +1,5 @@ +This is a testharness.js-based test. +FAIL src="http://[" assert_true: loadend event fired expected true got false +PASS src="" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html index 49e9565..c3c57ee3 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/invalid-src.html
@@ -10,12 +10,20 @@ var img = document.getElementById("brokenurl"); img.src = "http://["; var errorevent = false; + var loadendevent = false; // The errors should be queued in the event loop, so they should only trigger // after this block of code finishes, not during the img.src setter itself - img.addEventListener('error', t.step_func(function(){errorevent = true;})); - img.addEventListener('loadend', t.step_func_done(function() { + img.addEventListener('error', t.step_func(function() { + assert_false(loadendevent, "loadend should fire after error"); + errorevent = true; + t.step_timeout(t.step_func_done(function() { + assert_true(loadendevent, "loadend event fired"); + }), 0); + })); + img.addEventListener('loadend', t.step_func(function() { assert_true(errorevent, "error event fired"); + loadendevent = true; })); }, 'src="http://["'); @@ -31,8 +39,8 @@ // Queue this check in the event loop to check there is no loadend event // fired. t.step_timeout(t.step_func_done(function() { - assert_false(loadendevent, "loadend event should not fired"); - }), 0) + assert_false(loadendevent, "loadend event should not be fired"); + }), 0); })); img.addEventListener('loadend', t.step_func(function() { loadendevent = true;
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https-expected.txt b/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https-expected.txt index 1fa19f3..b9917d6 100644 --- a/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https-expected.txt +++ b/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https-expected.txt
@@ -1,4 +1,4 @@ This is a testharness.js-based test. -FAIL If a payment request is showing, but its document is navigated away (so no longer fully active), the payment request aborts. assert_throws: The iframe was navigated away, so showPromise must reject with AbortError function "function() { throw e }" threw object "InvalidStateError: Already called show() once" that is not a DOMException AbortError: property "code" is equal to 11, expected 20 +FAIL If a payment request is showing, but its document is navigated away (so no longer fully active), the payment sheet is dismissed. assert_true: Second payment should be pending but is rejected. expected true got false Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https.html b/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https.html index 4ba760ac..1c666ae 100644 --- a/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https.html +++ b/third_party/blink/web_tests/external/wpt/payment-request/rejects_if_not_active.https.html
@@ -65,14 +65,33 @@ await new Promise(resolve => { iframe.addEventListener("load", resolve); iframe.src = "/payment-request/resources/page2.html"; + // An implementation may optionally reject |showPromise|. + showPromise.catch(e => {}); }); - await promise_rejects( - t, - "AbortError", - showPromise, - "The iframe was navigated away, so showPromise must reject with AbortError" - ); - // We are done, so clean up. - iframe.remove(); -}, "If a payment request is showing, but its document is navigated away (so no longer fully active), the payment request aborts."); + + // The navigaton should have dismissed the previous payment request so it + // should be possible to show another one now. + const request2 = new iframe.contentWindow.PaymentRequest( + validMethods, validDetails); + const [showPromise2] = await test_driver.bless( + "show 2nd payment request", () => { + return [request2.show()]; + }); + + // Stop the test in 1 second if it has not rejected, which means that a + // payment sheet is successfully shown. + t.step_timeout(async () => { + // We are done, so clean up. + iframe.remove(); + t.done(); + }, 1000); + + // This should never settle because the payment sheet should be pending. + await showPromise2.then(() => { + assert_true(false, "Second payment should be pending but is resolved."); + }) + .catch(e => { + assert_true(false, "Second payment should be pending but is rejected."); + }); +}, "If a payment request is showing, but its document is navigated away (so no longer fully active), the payment sheet is dismissed."); </script>
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..ae36dc5d --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled.html new file mode 100644 index 0000000..bf9aae7 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-disabled.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body> +<input type='color' id='color' disabled> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +openPicker(document.getElementById('color'), () => testRunner.notifyDone(), () => testRunner.notifyDone()); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..d5370777 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..8c9ba85 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type.html new file mode 100644 index 0000000..6f66ca05 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-type.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body> +<input type='color' id='color'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +var colorControl = document.getElementById('color'); +openPicker(colorControl, openPickerCallback, openPickerCallback); + +function openPickerCallback() { + colorControl.type = 'text'; + testRunner.notifyDone(); +} +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..28ed0b3 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value.html new file mode 100644 index 0000000..ed9c32a0 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-set-value.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body> +<input type='color' id='color'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +var colorControl = document.getElementById('color'); +openPicker(colorControl, openPickerCallback, openPickerCallback); + +function openPickerCallback() { + colorControl.value = '#7EFFC9'; + testRunner.notifyDone(); +} +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..28ed0b3 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute.html new file mode 100644 index 0000000..eba6318 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body> +<input type='color' id='color' value='#7EFFC9'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +openPicker(document.getElementById('color'), () => testRunner.notifyDone(), () => testRunner.notifyDone()); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..816abce --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125.html new file mode 100644 index 0000000..20808cb --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body style='zoom: 1.25;'> +<input type='color' id='color'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +openPicker(document.getElementById('color'), () => testRunner.notifyDone(), () => testRunner.notifyDone()); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..1009037 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200.html new file mode 100644 index 0000000..8958196 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body style='zoom: 2;'> +<input type='color' id='color'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +openPicker(document.getElementById('color'), () => testRunner.notifyDone(), () => testRunner.notifyDone()); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance.html new file mode 100644 index 0000000..57deb34 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/color/color-picker-appearance.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<script> +testRunner.setUseMockTheme(false); +testRunner.waitUntilDone(); +</script> +<script src='../../../forms/resources/picker-common.js'></script> +</head> +<body> +<input type='color' id='color'> + +<p id='description' style='opacity: 0'></p> +<div id='console' style='opacity: 0'></div> + +<script> +openPicker(document.getElementById('color'), () => testRunner.notifyDone(), () => testRunner.notifyDone()); +</script> +</body> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ab738a7 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic.html new file mode 100644 index 0000000..ff8c3ee --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/select/select-inpage-appearance-basic.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<script src="../../../../resources/run-after-layout-and-paint.js"></script> +<script src="../../resources/common.js"></script> +<body> + +<!-- no style for reference --> +<select><option>unstyled</option></select> <br> + +<!-- rtl --> +<select dir="rtl"><option>right-to-left</option></select> <br> + +<!-- focused --> +<select id="focusTarget"><option>focused</option></select> <br> + +<!-- hovered --> +<select id="hoverTarget"><option>hovered</option></select> <br> + +<!-- disabled --> +<select disabled><option>disabled</option></select> <br> + +<!-- colored text --> +<select style="color:darkturquoise"><option>turquoise text</option></select> <br> + +<!-- border --> +<select style="border: 3px solid lime;"><option>thick lime border</option></select> +<select style="border-radius: 6px;"><option>border-radius: 6px</option></select> <br> + +<!-- background --> +<select style="background: linear-gradient(to bottom, #dea 0%,#9c7 44%,#494 100%);"><option>gradient background</option></select> <br> + +<!-- shadow --> +<select style="box-shadow: 4px 4px 10px rgba(255,0,0,0.5), inset 4px 4px 4px rgba(0,255,0,0.5);"><option>box-shadow</option></select> <br> + +<!-- arrow clipped --> +<select style="width: 24px; height: 5px;"><option>size: 8px</option></select> +<select style="width: 8px; height: 8px;"><option>size: 8px</option></select> <br> + +<!-- size --> +<select style="width: 16px; height: 16px;"><option>size: 16px</option></select> +<select style="width: 24px; height: 24px;"><option>size: 24px</option></select> +<select style="width: 26px; height: 20px;"><option>width: 26px; height: 20px</option></select> +<select style="width: 60px; height: 60px;"><option>size: 60px</option></select> <br> + +<!-- zoom --> +<select style="zoom: 1.5;"><option>zoom: 1.5</option></select> +<select style="zoom: 3;"><option>zoom: 3</option></select> <br> + +<script> +if (window.testRunner) + testRunner.setUseMockTheme(false); + +runAfterLayoutAndPaint(function() { + let focusTarget = document.getElementById('focusTarget'); + focusTarget.focus(); + + let hoverTarget = document.getElementById('hoverTarget'); + hoverOverElement(hoverTarget); +}, true); + +</script> +</body>
diff --git a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png index 29f53d9..a07b195 100644 --- a/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png +++ b/third_party/blink/web_tests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..cae9030b --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..56613c4 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..bf77f0f --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..62d6e53 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..84e18a6 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..58339057 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..0f8b6e7 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..99b2913 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..aa48940 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/disable-blink-gen-property-trees/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/blink/web_tests/platform/linux/virtual/disable-blink-gen-property-trees/compositing/reflections/nested-reflection-transformed2-expected.png new file mode 100644 index 0000000..a07b195 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/disable-blink-gen-property-trees/compositing/reflections/nested-reflection-transformed2-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..296ee6d --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..fa35ebb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..0e272dba --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..818c8eb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..8be1ad0 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..5da5e9a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..16f83e8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..137382d --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ae18601 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..a2801fa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..bb6759e6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..6e662a4 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..5dd8abf --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..110ae7d --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..d9c9e1e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..7f3031fd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..43ba613 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ae18601 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..56f7406c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..2733da2a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..43ae7a7 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..51b0121 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..610ef69a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..b721d6b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..18b6cbf1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ae18601 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..a733c46 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..074a611 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..b016880d --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..6b62615 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..af1fdc1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..6851718a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ae18601 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png index a344d8c..0e0f17f 100644 --- a/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png +++ b/third_party/blink/web_tests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..7f2c76f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..adb12a9 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..5c91ccbd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..555ac41 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..555ac41 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..2a6c5bc --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..d443bbe --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..4b7f6cd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..a733c46 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..074a611 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..b016880d --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..6b62615 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..af1fdc1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..6851718a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..ae18601 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png index 19c47c13..59c0c14 100644 --- a/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png +++ b/third_party/blink/web_tests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..77bcacc --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..a9f3592 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..5bc97ae --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..9c048d1 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..67cad60 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..5fb42b94 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..35875dd --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..6c6efce --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..08b61c3 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..7b0b758 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..10e1f0e --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..f4e5fb0 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png new file mode 100644 index 0000000..e05c588c --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-disabled-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png new file mode 100644 index 0000000..925bfd8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png new file mode 100644 index 0000000..1426c84 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-type-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png new file mode 100644 index 0000000..2247f00 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-set-value-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png new file mode 100644 index 0000000..2fb93b78 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-value-attribute-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png new file mode 100644 index 0000000..9fb3119 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png new file mode 100644 index 0000000..4b1dcc3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/color/color-picker-appearance-zoom200-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png new file mode 100644 index 0000000..e470884 --- /dev/null +++ b/third_party/blink/web_tests/virtual/controls-refresh/fast/forms/controls-new-ui/select/select-inpage-appearance-basic-expected.png Binary files differ
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium index 947051f..c9f5e78 100644 --- a/third_party/feed/README.chromium +++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@ Short name: feed URL: https://chromium.googlesource.com/feed Version: 0 -Revision: 6bd5f95e33bfa4ce1be7948c28d773e7d9247825 +Revision: 24f0eac37fc3d70ff410b0391f7a0cadc018df9a License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc index caf088df..3aa9935 100644 --- a/third_party/leveldatabase/env_chromium.cc +++ b/third_party/leveldatabase/env_chromium.cc
@@ -556,7 +556,7 @@ Options::Options() { // Note: Ensure that these default values correspond to those in -// components/services/leveldb/public/interfaces/leveldb.mojom. +// components/services/leveldb/public/mojom/leveldb.mojom. // TODO(cmumford) Create struct-trait for leveldb.mojom.OpenOptions to force // users to pass in a leveldb_env::Options instance (and it's defaults). //
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium index 7c7fb1e..6bca04b 100644 --- a/third_party/r8/README.chromium +++ b/third_party/r8/README.chromium
@@ -18,4 +18,8 @@ Turning off nullable inlining since it caused a performance regression here: crbug.com/965189. If R8 ever mitigates this, we can re-enable this. +Turning off outlining because it caused issues when synchronized proguarding +Trichrome due to illegal dex references (crbug.com/956839). Will only be used +for android_apk() targets that set `disable_r8_outlining = true`): + Patch is located at //third_party/r8/local_modifications.diff.
diff --git a/third_party/r8/local_modifications.diff b/third_party/r8/local_modifications.diff index 21c5806..ad76532 100644 --- a/third_party/r8/local_modifications.diff +++ b/third_party/r8/local_modifications.diff
@@ -1,5 +1,5 @@ diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java -index 2e1a254fd..8a2693e1f 100644 +index 2e1a254fd..d4b2b7e16 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java @@ -161,8 +161,8 @@ public class InternalOptions { @@ -24,3 +24,12 @@ public boolean enableClassInlining = true; public boolean enableClassStaticizer = true; public boolean enableInitializedClassesAnalysis = true; +@@ -868,7 +866,7 @@ public class InternalOptions { + public static final String CLASS_NAME = "com.android.tools.r8.GeneratedOutlineSupport"; + public static final String METHOD_PREFIX = "outline"; + +- public boolean enabled = true; ++ public boolean enabled = System.getProperty("com.android.tools.r8.disableOutlining") == null; + public int minSize = 3; + public int maxSize = 99; + public int threshold = 20;
diff --git a/third_party/webxr_test_pages/webxr-samples/index.published.html b/third_party/webxr_test_pages/webxr-samples/index.published.html index 6b64168..3ed2391 100644 --- a/third_party/webxr_test_pages/webxr-samples/index.published.html +++ b/third_party/webxr_test_pages/webxr-samples/index.published.html
@@ -144,6 +144,16 @@ let links = document.createElement('div'); links.classList.add('links'); + let liveLink = document.createElement('a'); + liveLink.href = page.path; + liveLink.innerHTML = 'Open'; + links.appendChild(liveLink); + + let polyfillLink = document.createElement('a'); + polyfillLink.href = page.path + '?allowPolyfill=1'; + polyfillLink.innerHTML = 'Open with Polyfill'; + links.appendChild(polyfillLink); + article.appendChild(links); mainElement.appendChild(article);
diff --git a/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.js b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.js index 0ef2fe18..8c43ca6 100644 --- a/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.js +++ b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.js
@@ -34,7 +34,7 @@ /** * @license - * webvr-polyfill-dpdb + * webvr-polyfill-dpdb * Copyright (c) 2017 Google * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,233 +103,153 @@ (global.WebXRPolyfill = factory()); }(this, (function () { 'use strict'; -var _global = typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : {}; +const _global = typeof global !== 'undefined' ? global : + typeof self !== 'undefined' ? self : + typeof window !== 'undefined' ? window : {}; -var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -}; - -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 inherits = function (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; -}; - - - - - - - - - - - -var possibleConstructorReturn = function (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; -}; - -var PRIVATE = Symbol('@@webxr-polyfill/EventTarget'); -var EventTarget = function () { - function EventTarget() { - classCallCheck(this, EventTarget); +const PRIVATE = Symbol('@@webxr-polyfill/EventTarget'); +class EventTarget { + constructor() { this[PRIVATE] = { - listeners: new Map() + listeners: new Map(), }; } - createClass(EventTarget, [{ - key: 'addEventListener', - value: function addEventListener(type, listener) { - if (typeof type !== 'string') { - throw new Error('`type` must be a string'); - } - if (typeof listener !== 'function') { - throw new Error('`listener` must be a function'); - } - var typedListeners = this[PRIVATE].listeners.get(type) || []; - typedListeners.push(listener); - this[PRIVATE].listeners.set(type, typedListeners); - } - }, { - key: 'removeEventListener', - value: function removeEventListener(type, listener) { - if (typeof type !== 'string') { - throw new Error('`type` must be a string'); - } - if (typeof listener !== 'function') { - throw new Error('`listener` must be a function'); - } - var typedListeners = this[PRIVATE].listeners.get(type) || []; - for (var i = typedListeners.length; i >= 0; i--) { - if (typedListeners[i] === listener) { - typedListeners.pop(); - } - } - } - }, { - key: 'dispatchEvent', - value: function dispatchEvent(type, event) { - var typedListeners = this[PRIVATE].listeners.get(type) || []; - var queue = []; - for (var i = 0; i < typedListeners.length; i++) { - queue[i] = typedListeners[i]; - } - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - try { - for (var _iterator = queue[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var listener = _step.value; - listener(event); - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - if (typeof this['on' + type] === 'function') { - this['on' + type](event); - } - } - }]); - return EventTarget; -}(); - -var PRIVATE$1 = Symbol('@@webxr-polyfill/XR'); -var XR = function (_EventTarget) { - inherits(XR, _EventTarget); - function XR(device) { - classCallCheck(this, XR); - var _this = possibleConstructorReturn(this, (XR.__proto__ || Object.getPrototypeOf(XR)).call(this)); - _this[PRIVATE$1] = { - device: device - }; - return _this; + addEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + typedListeners.push(listener); + this[PRIVATE].listeners.set(type, typedListeners); } - createClass(XR, [{ - key: 'requestDevice', - value: function requestDevice() { - return new Promise(function ($return, $error) { - var device; - return Promise.resolve(this[PRIVATE$1].device).then(function ($await_1) { - try { - device = $await_1; - if (device) { - return $return(device); - } - return $error(new Error('NotFoundError')); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this), $error); - }.bind(this)); + removeEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + for (let i = typedListeners.length; i >= 0; i--) { + if (typedListeners[i] === listener) { + typedListeners.pop(); + } } - }]); - return XR; -}(EventTarget); + } + dispatchEvent(type, event) { + const typedListeners = this[PRIVATE].listeners.get(type) || []; + const queue = []; + for (let i = 0; i < typedListeners.length; i++) { + queue[i] = typedListeners[i]; + } + for (let listener of queue) { + listener(event); + } + if (typeof this[`on${type}`] === 'function') { + this[`on${type}`](event); + } + } +} -var now = void 0; +const PRIVATE$1 = Symbol('@@webxr-polyfill/XR'); +const XRSessionModes = ['inline', 'immersive-vr', 'immersive-ar']; +const POLYFILL_REQUEST_SESSION_ERROR = +`Polyfill Error: Must call navigator.xr.supportsSession() with any XRSessionMode +or navigator.xr.requestSession('inline') prior to requesting an immersive +session. This is a limitation specific to the WebXR Polyfill and does not apply +to native implementations of the API.`; +class XR$1 extends EventTarget { + constructor(devicePromise) { + super(); + this[PRIVATE$1] = { + device: null, + devicePromise, + immersiveSession: null, + inlineSessions: new Set(), + }; + devicePromise.then((device) => { this[PRIVATE$1].device = device; }); + } + async supportsSession(mode) { + if (!this[PRIVATE$1].device) { + await this[PRIVATE$1].devicePromise; + } + if (mode != 'inline') { + if (!this[PRIVATE$1].device.supportsSession(mode)) { + return Promise.reject(null); + } + } + return Promise.resolve(null); + } + async requestSession(mode) { + if (!this[PRIVATE$1].device) { + if (mode != 'inline') { + throw new Error(POLYFILL_REQUEST_SESSION_ERROR); + } else { + await this[PRIVATE$1].devicePromise; + } + } + const sessionId = await this[PRIVATE$1].device.requestSession(mode); + const session = new XRSession(this[PRIVATE$1].device, mode, sessionId); + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.add(session); + } else { + this[PRIVATE$1].immersiveSession = session; + } + const onSessionEnd = () => { + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.delete(session); + } else { + this[PRIVATE$1].immersiveSession = null; + } + session.removeEventListener('end', onSessionEnd); + }; + session.addEventListener('end', onSessionEnd); + return session; + } +} + +let now; if ('performance' in _global === false) { - var startTime = Date.now(); - now = function now() { - return Date.now() - startTime; - }; + let startTime = Date.now(); + now = () => Date.now() - startTime; } else { - now = function now() { - return performance.now(); - }; + now = () => performance.now(); } var now$1 = now; -var PRIVATE$2 = Symbol('@@webxr-polyfill/XRPresentationContext'); -var XRPresentationContext = function () { - function XRPresentationContext(canvas, ctx, glAttribs) { - classCallCheck(this, XRPresentationContext); - this[PRIVATE$2] = { canvas: canvas, ctx: ctx, glAttribs: glAttribs }; - Object.assign(this, ctx); +const PRIVATE$2 = Symbol('@@webxr-polyfill/XRPose'); +class XRPose$1 { + constructor(transform, emulatedPosition) { + this[PRIVATE$2] = { + transform, + emulatedPosition, + }; } - createClass(XRPresentationContext, [{ - key: 'canvas', - get: function get$$1() { - return this[PRIVATE$2].canvas; - } - }]); - return XRPresentationContext; -}(); + get transform() { return this[PRIVATE$2].transform; } + get emulatedPosition() { return this[PRIVATE$2].emulatedPosition; } + _setTransform(transform) { this[PRIVATE$2].transform = transform; } +} -var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array; +const EPSILON = 0.000001; +let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; -var degree = Math.PI / 180; +const degree = Math.PI / 180; function create() { - var out = new ARRAY_TYPE(16); + let out = new ARRAY_TYPE(16); + if(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[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; } @@ -376,35 +296,23 @@ } function invert(out, a) { - var a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - var a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - var a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - var a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - var b00 = a00 * a11 - a01 * a10; - var b01 = a00 * a12 - a02 * a10; - var b02 = a00 * a13 - a03 * a10; - var b03 = a01 * a12 - a02 * a11; - var b04 = a01 * a13 - a03 * a11; - var b05 = a02 * a13 - a03 * a12; - var b06 = a20 * a31 - a21 * a30; - var b07 = a20 * a32 - a22 * a30; - var b08 = a20 * a33 - a23 * a30; - var b09 = a21 * a32 - a22 * a31; - var b10 = a21 * a33 - a23 * a31; - var b11 = a22 * a33 - a23 * a32; - var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + 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; + let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } @@ -430,45 +338,30 @@ function multiply(out, a, b) { - var a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - var a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - var a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - var a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - var 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; + 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 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; } @@ -484,22 +377,19 @@ function fromRotationTranslation(out, q, v) { - var x = q[0], - y = q[1], - z = q[2], - w = q[3]; - var x2 = x + x; - var y2 = y + y; - var z2 = z + z; - var xx = x * x2; - var xy = x * y2; - var xz = x * z2; - var yy = y * y2; - var yz = y * z2; - var zz = z * z2; - var wx = w * x2; - var wy = w * y2; - var wz = w * z2; + 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; @@ -527,15 +417,15 @@ } function getRotation(out, mat) { - var trace = mat[0] + mat[5] + mat[10]; - var S = 0; + 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]) { + } 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; @@ -561,8 +451,7 @@ function perspective(out, fovy, aspect, near, far) { - var f = 1.0 / Math.tan(fovy / 2); - var nf = 1 / (near - far); + let f = 1.0 / Math.tan(fovy / 2), nf; out[0] = f / aspect; out[1] = 0; out[2] = 0; @@ -573,927 +462,28 @@ 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; + 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; } -var PRIVATE$3 = Symbol('@@webxr-polyfill/XRDevicePose'); -var XRDevicePose = function () { - function XRDevicePose(polyfill) { - classCallCheck(this, XRDevicePose); - this[PRIVATE$3] = { - polyfill: polyfill, - leftViewMatrix: identity(new Float32Array(16)), - rightViewMatrix: identity(new Float32Array(16)), - poseModelMatrix: identity(new Float32Array(16)) - }; - } - createClass(XRDevicePose, [{ - key: 'getViewMatrix', - value: function getViewMatrix(view) { - switch (view.eye) { - case 'left': - return this[PRIVATE$3].leftViewMatrix; - case 'right': - return this[PRIVATE$3].rightViewMatrix; - } - throw new Error('view is not a valid XREye'); - } - }, { - key: 'updateFromFrameOfReference', - value: function updateFromFrameOfReference(frameOfRef) { - var pose = this[PRIVATE$3].polyfill.getBasePoseMatrix(); - var leftViewMatrix = this[PRIVATE$3].polyfill.getBaseViewMatrix('left'); - var rightViewMatrix = this[PRIVATE$3].polyfill.getBaseViewMatrix('right'); - if (pose) { - frameOfRef.transformBasePoseMatrix(this[PRIVATE$3].poseModelMatrix, pose); - } - if (leftViewMatrix && rightViewMatrix) { - frameOfRef.transformBaseViewMatrix(this[PRIVATE$3].leftViewMatrix, leftViewMatrix, this[PRIVATE$3].poseModelMatrix); - frameOfRef.transformBaseViewMatrix(this[PRIVATE$3].rightViewMatrix, rightViewMatrix, this[PRIVATE$3].poseModelMatrix); - } - } - }, { - key: 'poseModelMatrix', - get: function get$$1() { - return this[PRIVATE$3].poseModelMatrix; - } - }]); - return XRDevicePose; -}(); - -var PRIVATE$4 = Symbol('@@webxr-polyfill/XRViewport'); -var XRViewport = function () { - function XRViewport(target) { - classCallCheck(this, XRViewport); - this[PRIVATE$4] = { target: target }; - } - createClass(XRViewport, [{ - key: 'x', - get: function get$$1() { - return this[PRIVATE$4].target.x; - } - }, { - key: 'y', - get: function get$$1() { - return this[PRIVATE$4].target.y; - } - }, { - key: 'width', - get: function get$$1() { - return this[PRIVATE$4].target.width; - } - }, { - key: 'height', - get: function get$$1() { - return this[PRIVATE$4].target.height; - } - }]); - return XRViewport; -}(); - -var XREyes = ['left', 'right']; -var PRIVATE$5 = Symbol('@@webxr-polyfill/XRView'); -var XRView = function () { - function XRView(polyfill, eye, sessionId) { - classCallCheck(this, XRView); - if (!XREyes.includes(eye)) { - throw new Error('XREye must be one of: ' + XREyes); - } - var temp = Object.create(null); - var viewport = new XRViewport(temp); - this[PRIVATE$5] = { - polyfill: polyfill, - eye: eye, - viewport: viewport, - temp: temp, - sessionId: sessionId - }; - } - createClass(XRView, [{ - key: '_getViewport', - value: function _getViewport(layer) { - var viewport = this[PRIVATE$5].viewport; - if (this[PRIVATE$5].polyfill.getViewport(this[PRIVATE$5].sessionId, this.eye, layer, this[PRIVATE$5].temp)) { - return this[PRIVATE$5].viewport; - } - return undefined; - } - }, { - key: 'eye', - get: function get$$1() { - return this[PRIVATE$5].eye; - } - }, { - key: 'projectionMatrix', - get: function get$$1() { - return this[PRIVATE$5].polyfill.getProjectionMatrix(this.eye); - } - }]); - return XRView; -}(); - -var PRIVATE$6 = Symbol('@@webxr-polyfill/XRFrame'); -var XRFrame = function () { - function XRFrame(polyfill, session, sessionId) { - classCallCheck(this, XRFrame); - var devicePose = new XRDevicePose(polyfill); - var views = [new XRView(polyfill, 'left', sessionId)]; - if (session.immersive) { - views.push(new XRView(polyfill, 'right', sessionId)); - } - this[PRIVATE$6] = { - polyfill: polyfill, - devicePose: devicePose, - views: views, - session: session - }; - } - createClass(XRFrame, [{ - key: 'getDevicePose', - value: function getDevicePose(coordinateSystem) { - this[PRIVATE$6].devicePose.updateFromFrameOfReference(coordinateSystem); - return this[PRIVATE$6].devicePose; - } - }, { - key: 'getInputPose', - value: function getInputPose(inputSource, coordinateSystem) { - return this[PRIVATE$6].polyfill.getInputPose(inputSource, coordinateSystem); - } - }, { - key: 'session', - get: function get$$1() { - return this[PRIVATE$6].session; - } - }, { - key: 'views', - get: function get$$1() { - return this[PRIVATE$6].views; - } - }]); - return XRFrame; -}(); - -var PRIVATE$7 = Symbol('@@webxr-polyfill/XRStageBoundsPoint'); -var XRStageBoundsPoint = function () { - function XRStageBoundsPoint(x, z) { - classCallCheck(this, XRStageBoundsPoint); - this[PRIVATE$7] = { x: x, z: z }; - } - createClass(XRStageBoundsPoint, [{ - key: 'x', - get: function get$$1() { - return this[PRIVATE$7].x; - } - }, { - key: 'z', - get: function get$$1() { - return this[PRIVATE$7].z; - } - }]); - return XRStageBoundsPoint; -}(); - -var PRIVATE$8 = Symbol('@@webxr-polyfill/XRStageBounds'); -var XRStageBounds = function () { - function XRStageBounds(boundsData) { - classCallCheck(this, XRStageBounds); - var geometry = []; - for (var i = 0; i < boundsData.length; i += 2) { - geometry.push(new XRStageBoundsPoint(boundsData[i], boundsData[i + 1])); - } - this[PRIVATE$8] = { geometry: geometry }; - } - createClass(XRStageBounds, [{ - key: 'geometry', - get: function get$$1() { - return this[PRIVATE$8].geometry; - } - }]); - return XRStageBounds; -}(); - -var XRCoordinateSystem = function () { - function XRCoordinateSystem() { - classCallCheck(this, XRCoordinateSystem); - } - createClass(XRCoordinateSystem, [{ - key: 'getTransformTo', - value: function getTransformTo(other) { - throw new Error('Not yet supported'); - } - }]); - return XRCoordinateSystem; -}(); - -var DEFAULT_EMULATION_HEIGHT = 1.6; -var PRIVATE$9 = Symbol('@@webxr-polyfill/XRFrameOfReference'); -var XRFrameOfReferenceTypes = ['head-model', 'eye-level', 'stage']; -var XRFrameOfReferenceOptions = Object.freeze({ - disableStageEmulation: false, - stageEmulationHeight: 0 -}); -var XRFrameOfReference = function (_XRCoordinateSystem) { - inherits(XRFrameOfReference, _XRCoordinateSystem); - function XRFrameOfReference(polyfill, type, options, transform, bounds) { - classCallCheck(this, XRFrameOfReference); - options = Object.assign({}, XRFrameOfReferenceOptions, options); - if (!XRFrameOfReferenceTypes.includes(type)) { - throw new Error('XRFrameOfReferenceType must be one of ' + XRFrameOfReferenceTypes); - } - var _this = possibleConstructorReturn(this, (XRFrameOfReference.__proto__ || Object.getPrototypeOf(XRFrameOfReference)).call(this)); - if (type === 'stage' && options.disableStageEmulation && !transform) { - throw new Error('XRFrameOfReference cannot use \'stage\' type, if disabling emulation and platform does not provide'); - } - var _options = options, - disableStageEmulation = _options.disableStageEmulation, - stageEmulationHeight = _options.stageEmulationHeight; - var emulatedHeight = 0; - if (type === 'stage' && !transform) { - emulatedHeight = stageEmulationHeight !== 0 ? stageEmulationHeight : DEFAULT_EMULATION_HEIGHT; - } - if (type === 'stage' && !transform) { - transform = identity(new Float32Array(16)); - transform[13] = emulatedHeight; - } - _this[PRIVATE$9] = { - disableStageEmulation: disableStageEmulation, - stageEmulationHeight: stageEmulationHeight, - emulatedHeight: emulatedHeight, - type: type, - transform: transform, - polyfill: polyfill, - bounds: bounds - }; - _this.onboundschange = undefined; - return _this; - } - createClass(XRFrameOfReference, [{ - key: 'transformBasePoseMatrix', - value: function transformBasePoseMatrix(out, pose) { - if (this[PRIVATE$9].transform) { - multiply(out, this[PRIVATE$9].transform, pose); - return; - } - switch (this.type) { - case 'head-model': - if (out !== pose) { - copy(out, pose); - } - out[12] = out[13] = out[14] = 0; - return; - case 'eye-level': - if (out !== pose) { - copy(out, pose); - } - return; - } - } - }, { - key: 'transformBaseViewMatrix', - value: function transformBaseViewMatrix(out, view) { - var frameOfRef = this[PRIVATE$9].transform; - if (frameOfRef) { - invert(out, frameOfRef); - multiply(out, view, out); - } - else if (this.type === 'head-model') { - invert(out, view); - out[12] = 0; - out[13] = 0; - out[14] = 0; - invert(out, out); - return out; - } - else { - copy(out, view); - } - return out; - } - }, { - key: 'bounds', - get: function get$$1() { - return this[PRIVATE$9].bounds; - } - }, { - key: 'emulatedHeight', - get: function get$$1() { - return this[PRIVATE$9].emulatedHeight; - } - }, { - key: 'type', - get: function get$$1() { - return this[PRIVATE$9].type; - } - }]); - return XRFrameOfReference; -}(XRCoordinateSystem); - -var PRIVATE$10 = Symbol('@@webxr-polyfill/XRSession'); -var XRSessionCreationOptions = Object.freeze({ - immersive: false, - outputContext: undefined -}); -var validateSessionOptions = function validateSessionOptions(options) { - var immersive = options.immersive, - outputContext = options.outputContext; - if (!immersive && !outputContext) { - return false; - } - if (outputContext !== undefined && !(outputContext instanceof XRPresentationContext)) { - return false; - } - return true; -}; -var XRSession = function (_EventTarget) { - inherits(XRSession, _EventTarget); - function XRSession(polyfill, device, sessionOptions, id) { - classCallCheck(this, XRSession); - sessionOptions = Object.assign({}, XRSessionCreationOptions, sessionOptions); - var _this = possibleConstructorReturn(this, (XRSession.__proto__ || Object.getPrototypeOf(XRSession)).call(this)); - var _sessionOptions = sessionOptions, - immersive = _sessionOptions.immersive, - outputContext = _sessionOptions.outputContext; - _this[PRIVATE$10] = { - polyfill: polyfill, - device: device, - immersive: immersive, - outputContext: outputContext, - ended: false, - suspended: false, - suspendedCallback: null, - id: id - }; - var frame = new XRFrame(polyfill, _this, _this[PRIVATE$10].id); - _this[PRIVATE$10].frame = frame; - _this[PRIVATE$10].onPresentationEnd = function (sessionId) { - if (sessionId !== _this[PRIVATE$10].id) { - _this[PRIVATE$10].suspended = false; - _this.dispatchEvent('focus', { session: _this }); - var suspendedCallback = _this[PRIVATE$10].suspendedCallback; - _this[PRIVATE$10].suspendedCallback = null; - if (suspendedCallback) { - _this.requestAnimationFrame(suspendedCallback); - } - return; - } - _this[PRIVATE$10].ended = true; - polyfill.removeEventListener('@webvr-polyfill/vr-present-end', _this[PRIVATE$10].onPresentationEnd); - polyfill.removeEventListener('@webvr-polyfill/vr-present-start', _this[PRIVATE$10].onPresentationStart); - polyfill.removeEventListener('@@webvr-polyfill/input-select-start', _this[PRIVATE$10].onSelectStart); - polyfill.removeEventListener('@@webvr-polyfill/input-select-end', _this[PRIVATE$10].onSelectEnd); - _this.dispatchEvent('end', { session: _this }); - }; - polyfill.addEventListener('@@webxr-polyfill/vr-present-end', _this[PRIVATE$10].onPresentationEnd); - _this[PRIVATE$10].onPresentationStart = function (sessionId) { - if (sessionId === _this[PRIVATE$10].id) { - return; - } - _this[PRIVATE$10].suspended = true; - _this.dispatchEvent('blur', { session: _this }); - }; - polyfill.addEventListener('@@webxr-polyfill/vr-present-start', _this[PRIVATE$10].onPresentationStart); - _this[PRIVATE$10].onSelectStart = function (evt) { - if (evt.sessionId !== _this[PRIVATE$10].id) { - return; - } - _this.dispatchEvent('selectstart', { - frame: _this[PRIVATE$10].frame, - inputSource: evt.inputSource - }); - }; - polyfill.addEventListener('@@webxr-polyfill/input-select-start', _this[PRIVATE$10].onSelectStart); - _this[PRIVATE$10].onSelectEnd = function (evt) { - if (evt.sessionId !== _this[PRIVATE$10].id) { - return; - } - _this.dispatchEvent('selectend', { - frame: _this[PRIVATE$10].frame, - inputSource: evt.inputSource - }); - _this.dispatchEvent('select', { - frame: _this[PRIVATE$10].frame, - inputSource: evt.inputSource - }); - }; - polyfill.addEventListener('@@webxr-polyfill/input-select-end', _this[PRIVATE$10].onSelectEnd); - _this.onblur = undefined; - _this.onfocus = undefined; - _this.onresetpose = undefined; - _this.onend = undefined; - _this.onselect = undefined; - _this.onselectstart = undefined; - _this.onselectend = undefined; - return _this; - } - createClass(XRSession, [{ - key: 'requestFrameOfReference', - value: function requestFrameOfReference(type) { - var $args = arguments;return new Promise(function ($return, $error) { - var options, transform, bounds; - options = $args.length > 1 && $args[1] !== undefined ? $args[1] : {}; - if (this[PRIVATE$10].ended) { - return $return(); - } - options = Object.assign({}, XRFrameOfReferenceOptions, options); - if (!XRFrameOfReferenceTypes.includes(type)) { - return $error(new TypeError('XRFrameOfReferenceType must be one of ' + XRFrameOfReferenceTypes)); - } - transform = null; - bounds = null; - var $Try_1_Post = function () { - try { - if (type === 'stage' && transform) { - bounds = this[PRIVATE$10].polyfill.requestStageBounds(); - if (bounds) { - bounds = new XRStageBounds(bounds); - } - } - return $return(new XRFrameOfReference(this[PRIVATE$10].polyfill, type, options, transform, bounds)); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this);var $Try_1_Catch = function (e) { - try { - if (type !== 'stage' || options.disableStageEmulation) { - throw e; - } - return $Try_1_Post(); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this); - try { - return Promise.resolve(this[PRIVATE$10].polyfill.requestFrameOfReferenceTransform(type, options)).then(function ($await_2) { - try { - transform = $await_2; - return $Try_1_Post(); - } catch ($boundEx) { - return $Try_1_Catch($boundEx); - } - }.bind(this), $Try_1_Catch); - } catch (e) { - $Try_1_Catch(e); - } - }.bind(this)); - } - }, { - key: 'requestAnimationFrame', - value: function requestAnimationFrame(callback) { - var _this2 = this; - if (this[PRIVATE$10].ended) { - return; - } - if (this[PRIVATE$10].suspended && this[PRIVATE$10].suspendedCallback) { - return; - } - if (this[PRIVATE$10].suspended && !this[PRIVATE$10].suspendedCallback) { - this[PRIVATE$10].suspendedCallback = callback; - } - return this[PRIVATE$10].polyfill.requestAnimationFrame(function () { - _this2[PRIVATE$10].polyfill.onFrameStart(_this2[PRIVATE$10].id); - callback(now$1(), _this2[PRIVATE$10].frame); - _this2[PRIVATE$10].polyfill.onFrameEnd(_this2[PRIVATE$10].id); - }); - } - }, { - key: 'cancelAnimationFrame', - value: function cancelAnimationFrame(handle) { - if (this[PRIVATE$10].ended) { - return; - } - this[PRIVATE$10].polyfill.cancelAnimationFrame(handle); - } - }, { - key: 'getInputSources', - value: function getInputSources() { - return this[PRIVATE$10].polyfill.getInputSources(); - } - }, { - key: 'end', - value: function end() { - return new Promise(function ($return, $error) { - if (this[PRIVATE$10].ended) { - return $return(); - } - if (!this.immersive) { - this[PRIVATE$10].ended = true; - this[PRIVATE$10].polyfill.removeEventListener('@@webvr-polyfill/vr-present-start', this[PRIVATE$10].onPresentationStart); - this[PRIVATE$10].polyfill.removeEventListener('@@webvr-polyfill/vr-present-end', this[PRIVATE$10].onPresentationEnd); - this[PRIVATE$10].polyfill.removeEventListener('@@webvr-polyfill/input-select-start', this[PRIVATE$10].onSelectStart); - this[PRIVATE$10].polyfill.removeEventListener('@@webvr-polyfill/input-select-end', this[PRIVATE$10].onSelectEnd); - this.dispatchEvent('end', { session: this }); - } - return $return(this[PRIVATE$10].polyfill.endSession(this[PRIVATE$10].id)); - }.bind(this)); - } - }, { - key: 'device', - get: function get$$1() { - return this[PRIVATE$10].device; - } - }, { - key: 'immersive', - get: function get$$1() { - return this[PRIVATE$10].immersive; - } - }, { - key: 'outputContext', - get: function get$$1() { - return this[PRIVATE$10].outputContext; - } - }, { - key: 'depthNear', - get: function get$$1() { - return this[PRIVATE$10].polyfill.depthNear; - } - , - set: function set$$1(value) { - this[PRIVATE$10].polyfill.depthNear = value; - } - }, { - key: 'depthFar', - get: function get$$1() { - return this[PRIVATE$10].polyfill.depthFar; - } - , - set: function set$$1(value) { - this[PRIVATE$10].polyfill.depthFar = value; - } - }, { - key: 'environmentBlendMode', - get: function get$$1() { - return this[PRIVATE$10].polyfill.environmentBlendMode || 'opaque'; - } - }, { - key: 'baseLayer', - get: function get$$1() { - return this[PRIVATE$10].baseLayer; - } - , - set: function set$$1(value) { - if (this[PRIVATE$10].ended) { - return; - } - this[PRIVATE$10].baseLayer = value; - this[PRIVATE$10].polyfill.onBaseLayerSet(this[PRIVATE$10].id, value); - } - }]); - return XRSession; -}(EventTarget); - -var PRIVATE$11 = Symbol('@@webxr-polyfill/XRDevice'); -var XRDevice = function (_EventTarget) { - inherits(XRDevice, _EventTarget); - function XRDevice(polyfill) { - classCallCheck(this, XRDevice); - if (!polyfill) { - throw new Error('XRDevice must receive a PolyfilledXRDevice.'); - } - var _this = possibleConstructorReturn(this, (XRDevice.__proto__ || Object.getPrototypeOf(XRDevice)).call(this)); - _this[PRIVATE$11] = { - polyfill: polyfill, - immersiveSession: null, - nonImmersiveSessions: new Set() - }; - _this.ondeactive = undefined; - return _this; - } - createClass(XRDevice, [{ - key: 'supportsSession', - value: function supportsSession() { - var $args = arguments;return new Promise(function ($return, $error) { - var sessionOptions = $args.length > 0 && $args[0] !== undefined ? $args[0] : {}; - sessionOptions = Object.assign({}, XRSessionCreationOptions, sessionOptions); - if (!validateSessionOptions(sessionOptions)) { - return $return(Promise.reject(null)); - } - if (!this[PRIVATE$11].polyfill.supportsSession(sessionOptions)) { - return $return(Promise.reject(null)); - } - return $return(null); - }.bind(this)); - } - }, { - key: 'requestSession', - value: function requestSession(sessionOptions) { - return new Promise(function ($return, $error) { - var _this2, sessionId, session, onSessionEnd; - _this2 = this; - sessionOptions = Object.assign({}, XRSessionCreationOptions, sessionOptions); - if (!validateSessionOptions(sessionOptions)) { - return $error(new Error('NotSupportedError')); - } - if (this[PRIVATE$11].immersiveSession && sessionOptions.immersive) { - return $error(new Error('InvalidStateError')); - } - return Promise.resolve(this[PRIVATE$11].polyfill.requestSession(sessionOptions)).then(function ($await_1) { - try { - sessionId = $await_1; - session = new XRSession(this[PRIVATE$11].polyfill, this, sessionOptions, sessionId); - // TODO(crbug.com/958922): remove polyfill from tests. - if (sessionOptions.immersive) - session.mode = 'immersive-vr'; - else - session.mode = 'inline'; - if (sessionOptions.immersive) { - this[PRIVATE$11].immersiveSession = session; - } else { - this[PRIVATE$11].nonImmersiveSessions.add(session); - } - onSessionEnd = function onSessionEnd() { - if (session.immersive) { - _this2[PRIVATE$11].immersiveSession = null; - } else { - _this2[PRIVATE$11].nonImmersiveSessions.delete(session); - } - session.removeEventListener('end', onSessionEnd); - }; - session.addEventListener('end', onSessionEnd); - return $return(session); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this), $error); - }.bind(this)); - } - }]); - return XRDevice; -}(EventTarget); - -var domPointROExport = 'DOMPointReadOnly' in _global ? DOMPointReadOnly : null; -if (!domPointROExport) { - var PRIVATE$12 = Symbol('@@webxr-polyfill/DOMPointReadOnly'); - domPointROExport = function () { - function DOMPointReadOnly(x, y, z, w) { - classCallCheck(this, DOMPointReadOnly); - if (arguments.length === 1) { - this[PRIVATE$12] = { - x: x.x, - y: x.y, - z: x.z, - w: x.w - }; - } else if (arguments.length === 4) { - this[PRIVATE$12] = { - x: x, - y: y, - z: z, - w: w - }; - } else { - throw new TypeError('Must supply either 1 or 4 arguments'); - } - } - createClass(DOMPointReadOnly, [{ - key: 'x', - get: function get$$1() { - return this[PRIVATE$12].x; - } - }, { - key: 'y', - get: function get$$1() { - return this[PRIVATE$12].y; - } - }, { - key: 'z', - get: function get$$1() { - return this[PRIVATE$12].z; - } - }, { - key: 'w', - get: function get$$1() { - return this[PRIVATE$12].w; - } - }]); - return DOMPointReadOnly; - }(); -} -var DOMPointReadOnly$1 = domPointROExport; - -var PRIVATE$13 = Symbol('@@webxr-polyfill/XRInputPose'); -var XRInputPose = function () { - function XRInputPose(inputSourceImpl, hasGripMatrix) { - classCallCheck(this, XRInputPose); - this[PRIVATE$13] = { - inputSourceImpl: inputSourceImpl, - targetRay: new XRRay(), - gripMatrix: hasGripMatrix ? create() : null - }; - } - createClass(XRInputPose, [{ - key: 'targetRay', - get: function get$$1() { - return this[PRIVATE$13].targetRay; - } - , - set: function set$$1(value) { - this[PRIVATE$13].targetRay = value; - } - }, { - key: 'emulatedPosition', - get: function get$$1() { - return this[PRIVATE$13].inputSourceImpl.emulatedPosition; - } - }, { - key: 'gripMatrix', - get: function get$$1() { - return this[PRIVATE$13].gripMatrix; - } - }]); - return XRInputPose; -}(); - -var PRIVATE$14 = Symbol('@@webxr-polyfill/XRInputSource'); -var XRInputSource = function () { - function XRInputSource(impl) { - classCallCheck(this, XRInputSource); - this[PRIVATE$14] = { - impl: impl - }; - } - createClass(XRInputSource, [{ - key: 'handedness', - get: function get$$1() { - return this[PRIVATE$14].impl.handedness; - } - }, { - key: 'targetRayMode', - get: function get$$1() { - return this[PRIVATE$14].impl.targetRayMode; - } - }]); - return XRInputSource; -}(); - -var XRLayer = function () { - function XRLayer() { - classCallCheck(this, XRLayer); - } - createClass(XRLayer, [{ - key: "getViewport", - value: function getViewport(view) { - return view._getViewport(this); - } - }]); - return XRLayer; -}(); - -var POLYFILLED_COMPATIBLE_XR_DEVICE = Symbol('@@webxr-polyfill/polyfilled-compatible-xr-device'); -var COMPATIBLE_XR_DEVICE = Symbol('@@webxr-polyfill/compatible-xr-device'); - -var PRIVATE$15 = Symbol('@@webxr-polyfill/XRWebGLLayer'); -var XRWebGLLayerInit = Object.freeze({ - antialias: true, - depth: false, - stencil: false, - alpha: true, - multiview: false, - framebufferScaleFactor: 0 -}); -var XRWebGLLayer = function (_XRLayer) { - inherits(XRWebGLLayer, _XRLayer); - function XRWebGLLayer(session, context) { - var layerInit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - classCallCheck(this, XRWebGLLayer); - var config = Object.assign({}, XRWebGLLayerInit, layerInit); - if (!(session instanceof XRSession)) { - throw new Error('session must be a XRSession'); - } - if (session.ended) { - throw new Error('InvalidStateError'); - } - if (context[POLYFILLED_COMPATIBLE_XR_DEVICE]) { - if (context[COMPATIBLE_XR_DEVICE] !== session.device) { - throw new Error('InvalidStateError'); - } - } - var framebuffer = context.getParameter(context.FRAMEBUFFER_BINDING); - var _this = possibleConstructorReturn(this, (XRWebGLLayer.__proto__ || Object.getPrototypeOf(XRWebGLLayer)).call(this)); - _this[PRIVATE$15] = { - context: context, - config: config, - framebuffer: framebuffer - }; - return _this; - } - createClass(XRWebGLLayer, [{ - key: 'requestViewportScaling', - value: function requestViewportScaling(viewportScaleFactor) { - console.warn('requestViewportScaling is not yet implemented'); - } - }, { - key: 'context', - get: function get$$1() { - return this[PRIVATE$15].context; - } - }, { - key: 'antialias', - get: function get$$1() { - return this[PRIVATE$15].config.antialias; - } - }, { - key: 'depth', - get: function get$$1() { - return this[PRIVATE$15].config.depth; - } - }, { - key: 'stencil', - get: function get$$1() { - return this[PRIVATE$15].config.stencil; - } - }, { - key: 'alpha', - get: function get$$1() { - return this[PRIVATE$15].config.alpha; - } - }, { - key: 'multiview', - get: function get$$1() { - return false; - } - }, { - key: 'framebuffer', - get: function get$$1() { - return this[PRIVATE$15].framebuffer; - } - }, { - key: 'framebufferWidth', - get: function get$$1() { - return this[PRIVATE$15].context.drawingBufferWidth; - } - }, { - key: 'framebufferHeight', - get: function get$$1() { - return this[PRIVATE$15].context.drawingBufferHeight; - } - }]); - return XRWebGLLayer; -}(XRLayer); - -var API = { - XR: XR, - XRDevice: XRDevice, - XRSession: XRSession, - XRFrame: XRFrame, - XRView: XRView, - XRViewport: XRViewport, - XRDevicePose: XRDevicePose, - XRLayer: XRLayer, - XRWebGLLayer: XRWebGLLayer, - XRPresentationContext: XRPresentationContext, - XRCoordinateSystem: XRCoordinateSystem, - XRFrameOfReference: XRFrameOfReference, - XRStageBounds: XRStageBounds, - XRStageBoundsPoint: XRStageBoundsPoint, - XRInputPose: XRInputPose, - XRInputSource: XRInputSource, -}; - -var extendContextCompatibleXRDevice = function extendContextCompatibleXRDevice(Context) { - if (typeof Context.prototype.setCompatibleXRDevice === 'function') { - return false; - } - Context.prototype.setCompatibleXRDevice = function (xrDevice) { - var _this = this; - return new Promise(function (resolve, reject) { - if (xrDevice && typeof xrDevice.requestSession === 'function') { - resolve(); - } else { - reject(); - } - }).then(function () { - return _this[COMPATIBLE_XR_DEVICE] = xrDevice; - }); - }; - return true; -}; -var extendGetContext = function extendGetContext(Canvas) { - var getContext = HTMLCanvasElement.prototype.getContext; - HTMLCanvasElement.prototype.getContext = function (contextType, glAttribs) { - if (contextType === 'xrpresent') { - var _ctx = getContext.call(this, '2d', glAttribs); - return new XRPresentationContext(this, _ctx, glAttribs); - } - var ctx = getContext.call(this, contextType, glAttribs); - ctx[POLYFILLED_COMPATIBLE_XR_DEVICE] = true; - if (glAttribs && 'compatibleXRDevice' in glAttribs) { - ctx[COMPATIBLE_XR_DEVICE] = glAttribs.compatibleXRDevice; - } - return ctx; - }; -}; - function create$1() { - var out = new ARRAY_TYPE(3); - out[0] = 0; - out[1] = 0; - out[2] = 0; + let out = new ARRAY_TYPE(3); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } return out; } function clone$1(a) { @@ -1504,13 +494,13 @@ return out; } function length(a) { - var x = a[0]; - var y = a[1]; - var z = a[2]; - return Math.sqrt(x * x + y * y + z * z); + let x = a[0]; + let y = a[1]; + let z = a[2]; + return Math.sqrt(x*x + y*y + z*z); } function fromValues$1(x, y, z) { - var out = new ARRAY_TYPE(3); + let out = new ARRAY_TYPE(3); out[0] = x; out[1] = y; out[2] = z; @@ -1522,24 +512,14 @@ out[2] = a[2]; return out; } -function set$2(out, x, y, z) { - out[0] = x; - out[1] = y; - out[2] = z; - return out; -} + function add$1(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; return out; } -function subtract$1(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - return out; -} + @@ -1560,10 +540,10 @@ function normalize(out, a) { - var x = a[0]; - var y = a[1]; - var z = a[2]; - var len = x * x + y * y + z * z; + let x = a[0]; + let y = a[1]; + let z = a[2]; + let len = x*x + y*y + z*z; if (len > 0) { len = 1 / Math.sqrt(len); out[0] = a[0] * len; @@ -1576,6 +556,663 @@ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } 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; +} + + + + + + +function transformQuat(out, a, q) { + let qx = q[0], qy = q[1], qz = q[2], qw = q[3]; + let x = a[0], y = a[1], z = a[2]; + let uvx = qy * z - qz * y, + uvy = qz * x - qx * z, + uvz = qx * y - qy * x; + let uuvx = qy * uvz - qz * uvy, + uuvy = qz * uvx - qx * uvz, + uuvz = qx * uvy - qy * uvx; + let w2 = qw * 2; + uvx *= w2; + uvy *= w2; + uvz *= w2; + uuvx *= 2; + uuvy *= 2; + uuvz *= 2; + out[0] = x + uvx + uuvx; + out[1] = y + uvy + uuvy; + out[2] = z + uvz + uuvz; + return out; +} + + + +function angle(a, b) { + let tempA = fromValues$1(a[0], a[1], a[2]); + let tempB = fromValues$1(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); + } +} + + + + + + + + +const len = length; + +const forEach = (function() { + let vec = create$1(); + 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; + }; +})(); + +function create$2() { + let out = new ARRAY_TYPE(9); + if(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; +} + +function create$3() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} +function clone$3(a) { + let out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} +function fromValues$3(x, y, z, w) { + let out = new ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +} +function copy$3(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$1(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; +} + + + + + + + + + + + + + + + +const forEach$1 = (function() { + let vec = create$3(); + 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; + }; +})(); + +function create$4() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return 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; +} + +function multiply$4(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; +} + + + + +function slerp(out, a, b, t) { + 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; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + if ( (1.0 - cosom) > EPSILON ) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + 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; +} + +function invert$2(out, a) { + let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + let dot$$1 = a0*a0 + a1*a1 + a2*a2 + a3*a3; + let invDot = dot$$1 ? 1.0/dot$$1 : 0; + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +} + +function fromMat3(out, m) { + let fTrace = m[0] + m[4] + m[8]; + let fRoot; + if ( fTrace > 0.0 ) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; + out[0] = (m[5]-m[7])*fRoot; + out[1] = (m[6]-m[2])*fRoot; + out[2] = (m[1]-m[3])*fRoot; + } else { + 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; +} +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; +} + +const clone$4 = clone$3; +const fromValues$4 = fromValues$3; +const copy$4 = copy$3; + + + + + + + + + + +const normalize$2 = normalize$1; + + +const rotationTo = (function() { + let tmpvec3 = create$1(); + let xUnitVec3 = fromValues$1(1,0,0); + let yUnitVec3 = fromValues$1(0,1,0); + return function(out, a, b) { + let dot$$1 = dot(a, b); + if (dot$$1 < -0.999999) { + cross(tmpvec3, xUnitVec3, a); + if (len(tmpvec3) < 0.000001) + cross(tmpvec3, yUnitVec3, a); + normalize(tmpvec3, tmpvec3); + setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot$$1 > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot$$1; + return normalize$2(out, out); + } + }; +})(); +const sqlerp = (function () { + let temp1 = create$4(); + let temp2 = create$4(); + 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; + }; +}()); +const setAxes = (function() { + let matr = create$2(); + 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$2(out, fromMat3(out, matr)); + }; +})(); + +const PRIVATE$3 = Symbol('@@webxr-polyfill/XRRigidTransform'); +class XRRigidTransform$1 { + constructor() { + this[PRIVATE$3] = { + matrix: null, + position: null, + orientation: null, + inverse: null, + }; + if (arguments.length === 0) { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + } else if (arguments.length === 1) { + if (arguments[0] instanceof Float32Array) { + this[PRIVATE$3].matrix = arguments[0]; + } else { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: 0, y: 0, z: 0, w: 1 + }); + } + } else if (arguments.length === 2) { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = this._getPoint(arguments[1]); + } else { + throw new Error("Too many arguments!"); + } + if (this[PRIVATE$3].matrix) { + let position = create$1(); + getTranslation(position, this[PRIVATE$3].matrix); + this[PRIVATE$3].position = DOMPointReadOnly.fromPoint({ + x: position[0], + y: position[1], + z: position[2] + }); + let orientation = create$4(); + getRotation(orientation, this[PRIVATE$3].matrix); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: orientation[0], + y: orientation[1], + z: orientation[2], + w: orientation[3] + }); + } else { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + fromRotationTranslation( + this[PRIVATE$3].matrix, + fromValues$4( + this[PRIVATE$3].orientation.x, + this[PRIVATE$3].orientation.y, + this[PRIVATE$3].orientation.z, + this[PRIVATE$3].orientation.w), + fromValues$1( + this[PRIVATE$3].position.x, + this[PRIVATE$3].position.y, + this[PRIVATE$3].position.z) + ); + } + } + _getPoint(arg) { + if (arg instanceof DOMPointReadOnly) { + return arg; + } + return DOMPointReadOnly.fromPoint(arg); + } + get matrix() { return this[PRIVATE$3].matrix; } + get position() { return this[PRIVATE$3].position; } + get orientation() { return this[PRIVATE$3].orientation; } + get inverse() { + if (this[PRIVATE$3].inverse === null) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, this[PRIVATE$3].matrix); + this[PRIVATE$3].inverse = new XRRigidTransform$1(invMatrix); + this[PRIVATE$3].inverse[PRIVATE$3].inverse = this; + } + return this[PRIVATE$3].inverse; + } +} + +const PRIVATE$4 = Symbol('@@webxr-polyfill/XRViewerPose'); +class XRViewerPose extends XRPose$1 { + constructor(device) { + super(new XRRigidTransform$1(), false); + this[PRIVATE$4] = { + device, + leftViewMatrix: identity(new Float32Array(16)), + rightViewMatrix: identity(new Float32Array(16)), + poseModelMatrix: identity(new Float32Array(16)), + }; + } + get poseModelMatrix() { return this[PRIVATE$4].poseModelMatrix; } + getViewMatrix(view) { + switch (view.eye) { + case 'left': return this[PRIVATE$4].leftViewMatrix; + case 'right': return this[PRIVATE$4].rightViewMatrix; + } + throw new Error(`view is not a valid XREye`); + } + get views() { + return this[PRIVATE$4].views; + } + set views(value) { + this[PRIVATE$4].views = value; + } + updateFromReferenceSpace(refSpace) { + const pose = this[PRIVATE$4].device.getBasePoseMatrix(); + const leftViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('left'); + const rightViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('right'); + if (pose) { + refSpace.transformBasePoseMatrix(this[PRIVATE$4].poseModelMatrix, pose); + refSpace._adjustForOriginOffset(this[PRIVATE$4].poseModelMatrix); + super._setTransform(new XRRigidTransform$1(this[PRIVATE$4].poseModelMatrix)); + } + if (leftViewMatrix && rightViewMatrix) { + refSpace.transformBaseViewMatrix(this[PRIVATE$4].leftViewMatrix, + leftViewMatrix, + this[PRIVATE$4].poseModelMatrix); + refSpace.transformBaseViewMatrix(this[PRIVATE$4].rightViewMatrix, + rightViewMatrix, + this[PRIVATE$4].poseModelMatrix); + multiply(this[PRIVATE$4].leftViewMatrix, this[PRIVATE$4].leftViewMatrix, refSpace._originOffsetMatrix()); + multiply(this[PRIVATE$4].rightViewMatrix, this[PRIVATE$4].rightViewMatrix, refSpace._originOffsetMatrix()); + } + for (let view of this[PRIVATE$4].views) { + if (view.eye == "left") { + view._updateViewMatrix(this[PRIVATE$4].leftViewMatrix); + } else if (view.eye == "right") { + view._updateViewMatrix(this[PRIVATE$4].rightViewMatrix); + } + } + } +} + +const PRIVATE$5 = Symbol('@@webxr-polyfill/XRViewport'); +class XRViewport { + constructor(target) { + this[PRIVATE$5] = { target }; + } + get x() { return this[PRIVATE$5].target.x; } + get y() { return this[PRIVATE$5].target.y; } + get width() { return this[PRIVATE$5].target.width; } + get height() { return this[PRIVATE$5].target.height; } +} + +const XREyes = ['left', 'right']; +const PRIVATE$6 = Symbol('@@webxr-polyfill/XRView'); +class XRView { + constructor(device, eye, sessionId) { + if (!XREyes.includes(eye)) { + throw new Error(`XREye must be one of: ${XREyes}`); + } + const temp = Object.create(null); + const viewport = new XRViewport(temp); + this[PRIVATE$6] = { + device, + eye, + viewport, + temp, + sessionId, + transform: null, + }; + } + get eye() { return this[PRIVATE$6].eye; } + get projectionMatrix() { return this[PRIVATE$6].device.getProjectionMatrix(this.eye); } + get transform() { return this[PRIVATE$6].transform; } + _updateViewMatrix(viewMatrix) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, viewMatrix); + this[PRIVATE$6].transform = new XRRigidTransform$1(invMatrix); + } + _getViewport(layer) { + const viewport = this[PRIVATE$6].viewport; + if (this[PRIVATE$6].device.getViewport(this[PRIVATE$6].sessionId, + this.eye, + layer, + this[PRIVATE$6].temp)) { + return this[PRIVATE$6].viewport; + } + return undefined; + } +} + +var EPSILON$1 = 0.000001; +var ARRAY_TYPE$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array; + + +var degree$1 = Math.PI / 180; + +function create$7() { + var out = new ARRAY_TYPE$1(9); + if (ARRAY_TYPE$1 != 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; +} + +function create$9() { + var out = new ARRAY_TYPE$1(3); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} + +function length$3(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return Math.sqrt(x * x + y * y + z * z); +} +function fromValues$9(x, y, z) { + var out = new ARRAY_TYPE$1(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$3(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var len = x * x + y * y + z * z; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +} +function dot$3(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +function cross$1(out, a, b) { var ax = a[0], ay = a[1], az = a[2]; @@ -1591,73 +1228,25 @@ -function transformMat4(out, a, m) { - var x = a[0], - y = a[1], - z = a[2]; - var 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; -} - -function transformQuat(out, a, q) { - var qx = q[0], - qy = q[1], - qz = q[2], - qw = q[3]; - var x = a[0], - y = a[1], - z = a[2]; - var uvx = qy * z - qz * y, - uvy = qz * x - qx * z, - uvz = qx * y - qy * x; - var uuvx = qy * uvz - qz * uvy, - uuvy = qz * uvx - qx * uvz, - uuvz = qx * uvy - qy * uvx; - var w2 = qw * 2; - uvx *= w2; - uvy *= w2; - uvz *= w2; - uuvx *= 2; - uuvy *= 2; - uuvz *= 2; - out[0] = x + uvx + uuvx; - out[1] = y + uvy + uuvy; - out[2] = z + uvz + uuvz; - return out; -} - - - -function angle(a, b) { - var tempA = fromValues$1(a[0], a[1], a[2]); - var tempB = fromValues$1(b[0], b[1], b[2]); - normalize(tempA, tempA); - normalize(tempB, tempB); - var cosine = dot(tempA, tempB); - if (cosine > 1.0) { - return 0; - } else if (cosine < -1.0) { - return Math.PI; - } else { - return Math.acos(cosine); - } -} - - - -var sub$1 = subtract$1; -var len = length; -var forEach = function () { - var vec = create$1(); + + + + + + + + + + +var len$3 = length$3; + +var forEach$2 = function () { + var vec = create$9(); return function (a, stride, offset, count, fn, arg) { var i = void 0, l = void 0; @@ -1681,25 +1270,853 @@ }; }(); -var poseMatrixToXRRay = function poseMatrixToXRRay(poseMatrix) { - var rayOrigin = []; - var rayDirection = []; - set$2(rayOrigin, 0, 0, 0); - transformMat4(rayOrigin, rayOrigin, poseMatrix); - set$2(rayDirection, 0, 0, -1); - transformMat4(rayDirection, rayDirection, poseMatrix); - sub$1(rayDirection, rayDirection, rayOrigin); - normalize(rayDirection, rayDirection); - return new XRRay(new DOMPointReadOnly$1(rayOrigin[0], rayOrigin[1], rayOrigin[2], 1.0), new DOMPointReadOnly$1(rayDirection[0], rayDirection[1], rayDirection[2], 0.0), poseMatrix); +function create$10() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + +function normalize$4(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + var 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; +} + + + + + + + + + + + + + + + +var forEach$3 = function () { + var vec = create$10(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + 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; + }; +}(); + +function create$11() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return out; +} + +function setAxisAngle$1(out, axis, rad) { + rad = rad * 0.5; + var 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; +} + + + + + + +function slerp$1(out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = b[0], + by = b[1], + bz = b[2], + bw = b[3]; + var omega = void 0, + cosom = void 0, + sinom = void 0, + scale0 = void 0, + scale1 = void 0; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if (cosom < 0.0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } + if (1.0 - cosom > EPSILON$1) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + 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; +} + + + +function fromMat3$1(out, m) { + var fTrace = m[0] + m[4] + m[8]; + var fRoot = void 0; + if (fTrace > 0.0) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[0] = (m[5] - m[7]) * fRoot; + out[1] = (m[6] - m[2]) * fRoot; + out[2] = (m[1] - m[3]) * fRoot; + } else { + var i = 0; + if (m[4] > m[0]) i = 1; + if (m[8] > m[i * 3 + i]) i = 2; + var j = (i + 1) % 3; + var 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; +} + + + + + + + + + + + + + + + +var normalize$5 = normalize$4; + + +var rotationTo$1 = function () { + var tmpvec3 = create$9(); + var xUnitVec3 = fromValues$9(1, 0, 0); + var yUnitVec3 = fromValues$9(0, 1, 0); + return function (out, a, b) { + var dot = dot$3(a, b); + if (dot < -0.999999) { + cross$1(tmpvec3, xUnitVec3, a); + if (len$3(tmpvec3) < 0.000001) cross$1(tmpvec3, yUnitVec3, a); + normalize$3(tmpvec3, tmpvec3); + setAxisAngle$1(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 { + cross$1(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return normalize$5(out, out); + } + }; +}(); +var sqlerp$1 = function () { + var temp1 = create$11(); + var temp2 = create$11(); + return function (out, a, b, c, d, t) { + slerp$1(temp1, a, d, t); + slerp$1(temp2, b, c, t); + slerp$1(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; +}(); +var setAxes$1 = function () { + var matr = create$7(); + 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$5(out, fromMat3$1(out, matr)); + }; +}(); + +function create$13() { + var out = new ARRAY_TYPE$1(2); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var forEach$4 = function () { + var vec = create$13(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + 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; + }; +}(); + +const PRIVATE$7 = Symbol('@@webxr-polyfill/XRFrame'); +class XRFrame { + constructor(device, session, sessionId) { + const viewerPose = new XRViewerPose(device); + const views = [ + new XRView(device, 'left', sessionId), + ]; + if (session.immersive) { + views.push(new XRView(device, 'right', sessionId)); + } + viewerPose.views = views; + this[PRIVATE$7] = { + device, + viewerPose, + views, + session, + }; + } + get session() { return this[PRIVATE$7].session; } + get views() { return this[PRIVATE$7].views; } + getViewerPose(space) { + this[PRIVATE$7].viewerPose.updateFromReferenceSpace(space); + return this[PRIVATE$7].viewerPose; + } + getPose(space, baseSpace) { + if (space._specialType === "viewer") { + let viewerPose = this.getViewerPose(baseSpace); + return new XRPose( + new XRRigidTransform(viewerPose.poseModelMatrix), + viewerPose.emulatedPosition); + } + if (space._specialType === "target-ray" || space._specialType === "grip") { + return this[PRIVATE$7].device.getInputPose( + space._inputSource, baseSpace, space._specialType); + } + return null; + } +} + +const PRIVATE$8 = Symbol('@@webxr-polyfill/XRStageBoundsPoint'); +class XRStageBoundsPoint { + constructor(x, z) { + this[PRIVATE$8] = { x, z }; + } + get x() { return this[PRIVATE$8].x; } + get z() { return this[PRIVATE$8].z; } +} + +const PRIVATE$9 = Symbol('@@webxr-polyfill/XRStageBounds'); +class XRStageBounds { + constructor(boundsData) { + const geometry = []; + for (let i = 0; i < boundsData.length; i += 2) { + geometry.push(new XRStageBoundsPoint(boundsData[i], boundsData[i + 1])); + } + this[PRIVATE$9] = { geometry }; + } + get geometry() { return this[PRIVATE$9].geometry; } +} + +const PRIVATE$10 = Symbol('@@webxr-polyfill/XRSpace'); +class XRSpace { + constructor(specialType = null, inputSource = null) { + this[PRIVATE$10] = { + specialType, + inputSource, + }; + } + get _specialType() { + return this[PRIVATE$10].specialType; + } + get _inputSource() { + return this[PRIVATE$10].inputSource; + } +} + +const DEFAULT_EMULATION_HEIGHT = 1.6; +const PRIVATE$11 = Symbol('@@webxr-polyfill/XRReferenceSpace'); +const XRReferenceSpaceTypes = [ + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded' +]; +const XRReferenceSpaceOptions = Object.freeze({ + disableStageEmulation: false, + stageEmulationHeight: 0, +}); +class XRReferenceSpace extends XRSpace { + constructor(device, type, options, transform, bounds) { + options = Object.assign({}, XRReferenceSpaceOptions, options); + if (!XRReferenceSpaceTypes.includes(type)) { + throw new Error(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + super((type === 'viewer') ? 'viewer' : null); + if (this._isFloor(type) && options.disableStageEmulation && !transform) { + throw new Error(`XRReferenceSpace cannot use 'bounded-floor' type, if disabling emulation and platform does not provide`); + } + const { disableStageEmulation, stageEmulationHeight } = options; + let emulatedHeight = 0; + if (this._isFloor(type) && !transform) { + emulatedHeight = stageEmulationHeight !== 0 ? stageEmulationHeight : DEFAULT_EMULATION_HEIGHT; + } + if (this._isFloor(type) && !transform) { + transform = identity(new Float32Array(16)); + transform[13] = emulatedHeight; + } + if (!transform) { + transform = identity(new Float32Array(16)); + } + this[PRIVATE$11] = { + disableStageEmulation, + stageEmulationHeight, + emulatedHeight, + type, + transform, + device, + bounds, + options, + originOffset : identity(new Float32Array(16)), + }; + this.onboundschange = undefined; + } + _isFloor(type) { + return type === 'bounded-floor' || type === 'local-floor'; + } + get bounds() { return this[PRIVATE$11].bounds; } + get emulatedHeight() { return this[PRIVATE$11].emulatedHeight; } + get type() { return this[PRIVATE$11].type; } + transformBasePoseMatrix(out, pose) { + if (this[PRIVATE$11].transform) { + multiply(out, this[PRIVATE$11].transform, pose); + return; + } + switch (this.type) { + case 'local': + if (out !== pose) { + copy(out, pose); + } + return; + } + } + transformBaseViewMatrix(out, view) { + let frameOfRef = this[PRIVATE$11].transform; + if (frameOfRef) { + invert(out, frameOfRef); + multiply(out, view, out); + } + else { + copy(out, view); + } + return out; + } + _inverseOriginOffsetMatrix() { + let result = identity(new Float32Array(16)); + invert(result, this[PRIVATE$11].originOffset); + return result; + } + _originOffsetMatrix() { + return this[PRIVATE$11].originOffset; + } + _adjustForOriginOffset(transformMatrix) { + multiply(transformMatrix, this._inverseOriginOffsetMatrix(), transformMatrix); + } + getOffsetReferenceSpace(additionalOffset) { + let newSpace = new XRReferenceSpace( + this[PRIVATE$11].device, + this[PRIVATE$11].type, + this[PRIVATE$11].options, + this[PRIVATE$11].transform, + this[PRIVATE$11].bounds); + multiply(newSpace[PRIVATE$11].originOffset, this[PRIVATE$11].originOffset, additionalOffset.matrix); + return newSpace; + } +} + +const POLYFILLED_XR_COMPATIBLE = Symbol('@@webxr-polyfill/polyfilled-xr-compatible'); +const XR_COMPATIBLE = Symbol('@@webxr-polyfill/xr-compatible'); + +const PRIVATE$12 = Symbol('@@webxr-polyfill/XRWebGLLayer'); +const XRWebGLLayerInit = Object.freeze({ + antialias: true, + depth: false, + stencil: false, + alpha: true, + multiview: false, + ignoreDepthValues: false, + framebufferScaleFactor: 1.0, +}); +class XRWebGLLayer { + constructor(session, context, layerInit={}) { + const config = Object.assign({}, XRWebGLLayerInit, layerInit); + if (!(session instanceof XRSession$1)) { + throw new Error('session must be a XRSession'); + } + if (session.ended) { + throw new Error(`InvalidStateError`); + } + if (context[POLYFILLED_XR_COMPATIBLE]) { + if (context[XR_COMPATIBLE] !== true) { + throw new Error(`InvalidStateError`); + } + } + const framebuffer = context.getParameter(context.FRAMEBUFFER_BINDING); + this[PRIVATE$12] = { + context, + config, + framebuffer, + session, + }; + } + get context() { return this[PRIVATE$12].context; } + get antialias() { return this[PRIVATE$12].config.antialias; } + get ignoreDepthValues() { return true; } + get framebuffer() { return this[PRIVATE$12].framebuffer; } + get framebufferWidth() { return this[PRIVATE$12].context.drawingBufferWidth; } + get framebufferHeight() { return this[PRIVATE$12].context.drawingBufferHeight; } + get _session() { return this[PRIVATE$12].session; } + getViewport(view) { + return view._getViewport(this); + } +} + +const PRIVATE$13 = Symbol('@@webxr-polyfill/XRSession'); +class XRSession$1 extends EventTarget { + constructor(device, mode, id) { + super(); + let immersive = mode != 'inline'; + let outputContext = null; + this[PRIVATE$13] = { + device, + mode, + immersive, + outputContext, + ended: false, + suspended: false, + suspendedCallback: null, + id, + activeRenderState: null, + pendingRenderState: null, + }; + const frame = new XRFrame(device, this, this[PRIVATE$13].id); + this[PRIVATE$13].frame = frame; + this[PRIVATE$13].onPresentationEnd = sessionId => { + if (sessionId !== this[PRIVATE$13].id) { + this[PRIVATE$13].suspended = false; + this.dispatchEvent('focus', { session: this }); + const suspendedCallback = this[PRIVATE$13].suspendedCallback; + this[PRIVATE$13].suspendedCallback = null; + if (suspendedCallback) { + this.requestAnimationFrame(suspendedCallback); + } + return; + } + this[PRIVATE$13].ended = true; + device.removeEventListener('@webvr-polyfill/vr-present-end', this[PRIVATE$13].onPresentationEnd); + device.removeEventListener('@webvr-polyfill/vr-present-start', this[PRIVATE$13].onPresentationStart); + device.removeEventListener('@@webvr-polyfill/input-select-start', this[PRIVATE$13].onSelectStart); + device.removeEventListener('@@webvr-polyfill/input-select-end', this[PRIVATE$13].onSelectEnd); + this.dispatchEvent('end', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-end', this[PRIVATE$13].onPresentationEnd); + this[PRIVATE$13].onPresentationStart = sessionId => { + if (sessionId === this[PRIVATE$13].id) { + return; + } + this[PRIVATE$13].suspended = true; + this.dispatchEvent('blur', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-start', this[PRIVATE$13].onPresentationStart); + this[PRIVATE$13].onSelectStart = evt => { + if (evt.sessionId !== this[PRIVATE$13].id) { + return; + } + this.dispatchEvent('selectstart', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-start', this[PRIVATE$13].onSelectStart); + this[PRIVATE$13].onSelectEnd = evt => { + if (evt.sessionId !== this[PRIVATE$13].id) { + return; + } + this.dispatchEvent('selectend', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + this.dispatchEvent('select', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-end', this[PRIVATE$13].onSelectEnd); + this.onblur = undefined; + this.onfocus = undefined; + this.onresetpose = undefined; + this.onend = undefined; + this.onselect = undefined; + this.onselectstart = undefined; + this.onselectend = undefined; + } + get renderState() { return this[PRIVATE$13].activeRenderState; } + get immersive() { return this[PRIVATE$13].immersive; } + get outputContext() { return this[PRIVATE$13].outputContext; } + get depthNear() { return this[PRIVATE$13].device.depthNear; } + set depthNear(value) { this[PRIVATE$13].device.depthNear = value; } + get depthFar() { return this[PRIVATE$13].device.depthFar; } + set depthFar(value) { this[PRIVATE$13].device.depthFar = value; } + get environmentBlendMode() { + return this[PRIVATE$13].device.environmentBlendMode || 'opaque'; + } + get baseLayer() { return this[PRIVATE$13].baseLayer; } + set baseLayer(value) { + if (this[PRIVATE$13].ended) { + return; + } + this[PRIVATE$13].baseLayer = value; + this[PRIVATE$13].device.onBaseLayerSet(this[PRIVATE$13].id, value); + } + async requestReferenceSpace(type, options={}) { + if (this[PRIVATE$13].ended) { + return; + } + options = Object.assign({}, XRReferenceSpaceOptions, options); + if (!XRReferenceSpaceTypes.includes(type)) { + throw new TypeError(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + let transform = null; + let bounds = null; + try { + transform = await this[PRIVATE$13].device.requestFrameOfReferenceTransform(type, options); + } catch (e) { + if (type !== 'stage' || options.disableStageEmulation) { + throw e; + } + } + if (type === 'stage' && transform) { + bounds = this[PRIVATE$13].device.requestStageBounds(); + if (bounds) { + bounds = new XRStageBounds(bounds); + } + } + return new XRReferenceSpace(this[PRIVATE$13].device, type, options, transform, bounds); + } + requestAnimationFrame(callback) { + if (this[PRIVATE$13].ended) { + return; + } + if (this[PRIVATE$13].suspended && this[PRIVATE$13].suspendedCallback) { + return; + } + if (this[PRIVATE$13].suspended && !this[PRIVATE$13].suspendedCallback) { + this[PRIVATE$13].suspendedCallback = callback; + } + return this[PRIVATE$13].device.requestAnimationFrame(() => { + if (this[PRIVATE$13].pendingRenderState !== null) { + this[PRIVATE$13].activeRenderState = this[PRIVATE$13].pendingRenderState; + this[PRIVATE$13].pendingRenderState = null; + if (this[PRIVATE$13].activeRenderState.baseLayer) { + this[PRIVATE$13].device.onBaseLayerSet( + this[PRIVATE$13].id, + this[PRIVATE$13].activeRenderState.baseLayer); + } + if (this[PRIVATE$13].activeRenderState.inlineVerticalFieldOfView) { + this[PRIVATE$13].device.onInlineVerticalFieldOfViewSet( + this[PRIVATE$13].id, + this[PRIVATE$13].activeRenderState.inlineVerticalFieldOfView); + } + } + this[PRIVATE$13].device.onFrameStart(this[PRIVATE$13].id); + callback(now$1(), this[PRIVATE$13].frame); + this[PRIVATE$13].device.onFrameEnd(this[PRIVATE$13].id); + }); + } + cancelAnimationFrame(handle) { + if (this[PRIVATE$13].ended) { + return; + } + this[PRIVATE$13].device.cancelAnimationFrame(handle); + } + get inputSources() { + return this[PRIVATE$13].device.getInputSources(); + } + async end() { + if (this[PRIVATE$13].ended) { + return; + } + if (!this.immersive) { + this[PRIVATE$13].ended = true; + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/vr-present-start', + this[PRIVATE$13].onPresentationStart); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/vr-present-end', + this[PRIVATE$13].onPresentationEnd); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/input-select-start', + this[PRIVATE$13].onSelectStart); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/input-select-end', + this[PRIVATE$13].onSelectEnd); + this.dispatchEvent('end', { session: this }); + } + return this[PRIVATE$13].device.endSession(this[PRIVATE$13].id); + } + updateRenderState(newState) { + if (this[PRIVATE$13].ended) { + const message = "Can't call updateRenderState on an XRSession " + + "that has already ended."; + throw new Error(message); + } + if (newState.baseLayer && (newState.baseLayer._session !== this)) { + const message = "Called updateRenderState with a base layer that was " + + "created by a different session."; + throw new Error(message); + } + const fovSet = (newState.inlineVerticalFieldOfView !== null) && + (newState.inlineVerticalFieldOfView !== undefined); + if (fovSet) { + if (this[PRIVATE$13].immersive) { + const message = "inlineVerticalFieldOfView must not be set for an " + + "XRRenderState passed to updateRenderState for an " + + "immersive session."; + throw new Error(message); + } else { + newState.inlineVerticalFieldOfView = Math.min( + 3.13, Math.max(0.01, newState.inlineVerticalFieldOfView)); + } + } + if (this[PRIVATE$13].pendingRenderState === null) { + this[PRIVATE$13].pendingRenderState = Object.assign( + {}, this[PRIVATE$13].activeRenderState, newState); + } + } +} + +const PRIVATE$14 = Symbol('@@webxr-polyfill/XRInputSource'); +class XRInputSource { + constructor(impl) { + this[PRIVATE$14] = { + impl, + gripSpace: new XRSpace("grip", this), + targetRaySpace: new XRSpace("target-ray", this) + }; + } + get handedness() { return this[PRIVATE$14].impl.handedness; } + get targetRayMode() { return this[PRIVATE$14].impl.targetRayMode; } + get gripSpace() { + let mode = this[PRIVATE$14].impl.targetRayMode; + if (mode === "gaze" || mode === "screen") { + return null; + } + return this[PRIVATE$14].gripSpace; + } + get targetRaySpace() { return this[PRIVATE$14].targetRaySpace; } + get gamepad() { return this[PRIVATE$14].impl.gamepad; } +} + +const PRIVATE$15 = Symbol('@@webxr-polyfill/XRRenderState'); +const XRRenderStateInit = Object.freeze({ + depthNear: 0.1, + depthFar: 1000.0, + inlineVerticalFieldOfView: null, + baseLayer: null +}); +class XRRenderState { + constructor(stateInit = {}) { + const config = Object.assign({}, XRRenderStateInit, stateInit); + this[PRIVATE$15] = { config }; + } + get depthNear() { return this[PRIVATE$15].depthNear; } + get depthFar() { return this[PRIVATE$15].depthFar; } + get inlineVerticalFieldOfView() { return this[PRIVATE$15].inlineVerticalFieldOfView; } + get baseLayer() { return this[PRIVATE$15].baseLayer; } +} + +var API = { + XR: XR$1, + XRSession: XRSession$1, + XRFrame, + XRView, + XRViewport, + XRViewerPose, + XRWebGLLayer, + XRSpace, + XRReferenceSpace, + XRStageBounds, + XRStageBoundsPoint, + XRInputSource, + XRRenderState, + XRRigidTransform: XRRigidTransform$1, + XRPose: XRPose$1, }; -var isMobile = function isMobile(global) { + +const polyfillMakeXRCompatible = Context => { + if (typeof Context.prototype.makeXRCompatible === 'function') { + return false; + } + Context.prototype.makeXRCompatible = function () { + this[XR_COMPATIBLE] = true; + return Promise.resolve(); + }; + return true; +}; +const polyfillGetContext = (Canvas) => { + const getContext = Canvas.prototype.getContext; + Canvas.prototype.getContext = function (contextType, glAttribs) { + const ctx = getContext.call(this, contextType, glAttribs); + ctx[POLYFILLED_XR_COMPATIBLE] = true; + if (glAttribs && ('xrCompatible' in glAttribs)) { + ctx[XR_COMPATIBLE] = glAttribs.xrCompatible; + } + return ctx; + }; +}; + +const isImageBitmapSupported = global => + !!(global.ImageBitmapRenderingContext && + global.createImageBitmap); +const isMobile = global => { var check = false; - (function (a) { - if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; - })(global.navigator.userAgent || global.navigator.vendor || global.opera); + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true;})(global.navigator.userAgent||global.navigator.vendor||global.opera); return check; }; -var applyCanvasStylesForMinimalRendering = function applyCanvasStylesForMinimalRendering(canvas) { +const applyCanvasStylesForMinimalRendering = canvas => { canvas.style.display = 'block'; canvas.style.position = 'absolute'; canvas.style.width = canvas.style.height = '1px'; @@ -1777,8 +2194,8 @@ }(); var MIN_TIMESTEP = 0.001; var MAX_TIMESTEP = 1; -var base64 = function base64(mimeType, _base) { - return 'data:' + mimeType + ';base64,' + _base; +var dataUri = function dataUri(mimeType, svg) { + return 'data:' + mimeType + ',' + encodeURIComponent(svg); }; var lerp = function lerp(a, b, t) { return a + (b - a) * t; @@ -3381,8 +3798,8 @@ } DeviceInfo.Viewers = Viewers; var format = 1; -var last_updated = "2018-02-20T22:55:10Z"; -var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}]; +var last_updated = "2018-12-10T17:01:42Z"; +var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2 XL/*"},{"ua":"Pixel 2 XL"}],"dpi":537.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3 XL/*"},{"ua":"Pixel 3 XL"}],"dpi":[558.5,553.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3/*"},{"ua":"Pixel 3"}],"dpi":442.4,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2/*"},{"ua":"Pixel 2"}],"dpi":441,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/BLN-L24/*"},{"ua":"HONORBLN-L24"}],"dpi":480,"bw":4,"ac":500},{"type":"android","rules":[{"mdmh":"Huawei/*/BKL-L09/*"},{"ua":"BKL-L09"}],"dpi":403,"bw":3.47,"ac":500},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955F/*"},{"ua":"SM-G955F"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}]; var DPDB_CACHE = { format: format, last_updated: last_updated, @@ -3457,7 +3874,7 @@ var matched = false; for (var j = 0; j < device.rules.length; j++) { var rule = device.rules[j]; - if (this.matchRule_(rule, userAgent, width, height)) { + if (this.ruleMatches_(rule, userAgent, width, height)) { matched = true; break; } @@ -3470,8 +3887,9 @@ console.warn('No DPDB device match.'); return null; }; -Dpdb.prototype.matchRule_ = function (rule, ua, screenWidth, screenHeight) { +Dpdb.prototype.ruleMatches_ = function (rule, ua, screenWidth, screenHeight) { if (!rule.ua && !rule.res) return false; + if (rule.ua && rule.ua.substring(0, 2) === 'SM') rule.ua = rule.ua.substring(0, 7); if (rule.ua && ua.indexOf(rule.ua) < 0) return false; if (rule.res) { if (!rule.res[0] || !rule.res[1]) return false; @@ -3807,18 +4225,6 @@ var SENSOR_FREQUENCY = 60; var X_AXIS = new Vector3(1, 0, 0); var Z_AXIS = new Vector3(0, 0, 1); -var orientation = {}; -if (screen.orientation) { - orientation = screen.orientation; -} else if (screen.msOrientation) { - orientation = screen.msOrientation; -} else { - Object.defineProperty(orientation, 'angle', { - get: function get$$1() { - return window.orientation || 0; - } - }); -} var SENSOR_TO_VR = new Quaternion(); SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2); SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2)); @@ -3832,12 +4238,9 @@ this.api = null; this.errors = []; this._sensorQ = new Quaternion(); - this._worldToScreenQ = new Quaternion(); this._outQ = new Quaternion(); this._onSensorRead = this._onSensorRead.bind(this); this._onSensorError = this._onSensorError.bind(this); - this._onOrientationChange = this._onOrientationChange.bind(this); - this._onOrientationChange(); this.init(); } createClass(PoseSensor, [{ @@ -3845,7 +4248,10 @@ value: function init() { var sensor = null; try { - sensor = new RelativeOrientationSensor({ frequency: SENSOR_FREQUENCY }); + sensor = new RelativeOrientationSensor({ + frequency: SENSOR_FREQUENCY, + referenceFrame: 'screen' + }); sensor.addEventListener('error', this._onSensorError); } catch (error) { this.errors.push(error); @@ -3865,7 +4271,6 @@ this.sensor.addEventListener('reading', this._onSensorRead); this.sensor.start(); } - window.addEventListener('orientationchange', this._onOrientationChange); } }, { key: 'useDeviceMotion', @@ -3894,7 +4299,6 @@ var out = this._outQ; out.copy(SENSOR_TO_VR); out.multiply(this._sensorQ); - out.multiply(this._worldToScreenQ); if (this.config.YAW_ONLY) { out.x = out.z = 0; out.normalize(); @@ -3921,16 +4325,10 @@ }, { key: '_onSensorRead', value: function _onSensorRead() {} - }, { - key: '_onOrientationChange', - value: function _onOrientationChange() { - var angle = -orientation.angle * Math.PI / 180; - this._worldToScreenQ.setFromAxisAngle(Z_AXIS, angle); - } }]); return PoseSensor; }(); -var rotateInstructionsAsset = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; +var rotateInstructionsAsset = "<svg width='198' height='240' viewBox='0 0 198 240' xmlns='http://www.w3.org/2000/svg'><g fill='none' fill-rule='evenodd'><path d='M149.625 109.527l6.737 3.891v.886c0 .177.013.36.038.549.01.081.02.162.027.242.14 1.415.974 2.998 2.105 3.999l5.72 5.062.081-.09s4.382-2.53 5.235-3.024l25.97 14.993v54.001c0 .771-.386 1.217-.948 1.217-.233 0-.495-.076-.772-.236l-23.967-13.838-.014.024-27.322 15.775-.85-1.323c-4.731-1.529-9.748-2.74-14.951-3.61a.27.27 0 0 0-.007.024l-5.067 16.961-7.891 4.556-.037-.063v27.59c0 .772-.386 1.217-.948 1.217-.232 0-.495-.076-.772-.236l-42.473-24.522c-.95-.549-1.72-1.877-1.72-2.967v-1.035l-.021.047a5.111 5.111 0 0 0-1.816-.399 5.682 5.682 0 0 0-.546.001 13.724 13.724 0 0 1-1.918-.041c-1.655-.153-3.2-.6-4.404-1.296l-46.576-26.89.005.012-10.278-18.75c-1.001-1.827-.241-4.216 1.698-5.336l56.011-32.345a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.659 3.227 1.853l.005-.003.227.413-.006.004a9.63 9.63 0 0 0 1.477 2.018l.277.27c1.914 1.85 4.468 2.801 7.113 2.801 1.949 0 3.948-.517 5.775-1.572.013 0 7.319-4.219 7.319-4.219a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.658 3.226 1.853l3.25 5.928.022-.018 6.785 3.917-.105-.182 46.881-26.965m0-1.635c-.282 0-.563.073-.815.218l-46.169 26.556-5.41-3.124-3.005-5.481c-.913-1.667-2.699-2.702-4.66-2.703-1.011 0-2.02.274-2.917.792a3825 3825 0 0 1-7.275 4.195l-.044.024a9.937 9.937 0 0 1-4.957 1.353c-2.292 0-4.414-.832-5.976-2.342l-.252-.245a7.992 7.992 0 0 1-1.139-1.534 1.379 1.379 0 0 0-.06-.122l-.227-.414a1.718 1.718 0 0 0-.095-.154c-.938-1.574-2.673-2.545-4.571-2.545-1.011 0-2.02.274-2.917.792L3.125 155.502c-2.699 1.559-3.738 4.94-2.314 7.538l10.278 18.75c.177.323.448.563.761.704l46.426 26.804c1.403.81 3.157 1.332 5.072 1.508a15.661 15.661 0 0 0 2.146.046 4.766 4.766 0 0 1 .396 0c.096.004.19.011.283.022.109 1.593 1.159 3.323 2.529 4.114l42.472 24.522c.524.302 1.058.455 1.59.455 1.497 0 2.583-1.2 2.583-2.852v-26.562l7.111-4.105a1.64 1.64 0 0 0 .749-.948l4.658-15.593c4.414.797 8.692 1.848 12.742 3.128l.533.829a1.634 1.634 0 0 0 2.193.531l26.532-15.317L193 192.433c.523.302 1.058.455 1.59.455 1.497 0 2.583-1.199 2.583-2.852v-54.001c0-.584-.312-1.124-.818-1.416l-25.97-14.993a1.633 1.633 0 0 0-1.636.001c-.606.351-2.993 1.73-4.325 2.498l-4.809-4.255c-.819-.725-1.461-1.933-1.561-2.936a7.776 7.776 0 0 0-.033-.294 2.487 2.487 0 0 1-.023-.336v-.886c0-.584-.312-1.123-.817-1.416l-6.739-3.891a1.633 1.633 0 0 0-.817-.219' fill='#455A64'/><path d='M96.027 132.636l46.576 26.891c1.204.695 1.979 1.587 2.242 2.541l-.01.007-81.374 46.982h-.001c-1.654-.152-3.199-.6-4.403-1.295l-46.576-26.891 83.546-48.235' fill='#FAFAFA'/><path d='M63.461 209.174c-.008 0-.015 0-.022-.002-1.693-.156-3.228-.609-4.441-1.309l-46.576-26.89a.118.118 0 0 1 0-.203l83.546-48.235a.117.117 0 0 1 .117 0l46.576 26.891c1.227.708 2.021 1.612 2.296 2.611a.116.116 0 0 1-.042.124l-.021.016-81.375 46.981a.11.11 0 0 1-.058.016zm-50.747-28.303l46.401 26.79c1.178.68 2.671 1.121 4.32 1.276l81.272-46.922c-.279-.907-1.025-1.73-2.163-2.387l-46.517-26.857-83.313 48.1z' fill='#607D8B'/><path d='M148.327 165.471a5.85 5.85 0 0 1-.546.001c-1.894-.083-3.302-1.038-3.145-2.132a2.693 2.693 0 0 0-.072-1.105l-81.103 46.822c.628.058 1.272.073 1.918.042.182-.009.364-.009.546-.001 1.894.083 3.302 1.038 3.145 2.132l79.257-45.759' fill='#FFF'/><path d='M69.07 211.347a.118.118 0 0 1-.115-.134c.045-.317-.057-.637-.297-.925-.505-.61-1.555-1.022-2.738-1.074a5.966 5.966 0 0 0-.535.001 14.03 14.03 0 0 1-1.935-.041.117.117 0 0 1-.103-.092.116.116 0 0 1 .055-.126l81.104-46.822a.117.117 0 0 1 .171.07c.104.381.129.768.074 1.153-.045.316.057.637.296.925.506.61 1.555 1.021 2.739 1.073.178.008.357.008.535-.001a.117.117 0 0 1 .064.218l-79.256 45.759a.114.114 0 0 1-.059.016zm-3.405-2.372c.089 0 .177.002.265.006 1.266.056 2.353.488 2.908 1.158.227.274.35.575.36.882l78.685-45.429c-.036 0-.072-.001-.107-.003-1.267-.056-2.354-.489-2.909-1.158-.282-.34-.402-.724-.347-1.107a2.604 2.604 0 0 0-.032-.91L63.846 208.97a13.91 13.91 0 0 0 1.528.012c.097-.005.194-.007.291-.007z' fill='#607D8B'/><path d='M2.208 162.134c-1.001-1.827-.241-4.217 1.698-5.337l56.011-32.344c1.939-1.12 4.324-.546 5.326 1.281l.232.41a9.344 9.344 0 0 0 1.47 2.021l.278.27c3.325 3.214 8.583 3.716 12.888 1.23l7.319-4.22c1.94-1.119 4.324-.546 5.325 1.282l3.25 5.928-83.519 48.229-10.278-18.75z' fill='#FAFAFA'/><path d='M12.486 181.001a.112.112 0 0 1-.031-.005.114.114 0 0 1-.071-.056L2.106 162.19c-1.031-1.88-.249-4.345 1.742-5.494l56.01-32.344a4.328 4.328 0 0 1 2.158-.588c1.415 0 2.65.702 3.311 1.882.01.008.018.017.024.028l.227.414a.122.122 0 0 1 .013.038 9.508 9.508 0 0 0 1.439 1.959l.275.266c1.846 1.786 4.344 2.769 7.031 2.769 1.977 0 3.954-.538 5.717-1.557a.148.148 0 0 1 .035-.013l7.284-4.206a4.321 4.321 0 0 1 2.157-.588c1.427 0 2.672.716 3.329 1.914l3.249 5.929a.116.116 0 0 1-.044.157l-83.518 48.229a.116.116 0 0 1-.059.016zm49.53-57.004c-.704 0-1.41.193-2.041.557l-56.01 32.345c-1.882 1.086-2.624 3.409-1.655 5.179l10.221 18.645 83.317-48.112-3.195-5.829c-.615-1.122-1.783-1.792-3.124-1.792a4.08 4.08 0 0 0-2.04.557l-7.317 4.225a.148.148 0 0 1-.035.013 11.7 11.7 0 0 1-5.801 1.569c-2.748 0-5.303-1.007-7.194-2.835l-.278-.27a9.716 9.716 0 0 1-1.497-2.046.096.096 0 0 1-.013-.037l-.191-.347a.11.11 0 0 1-.023-.029c-.615-1.123-1.783-1.793-3.124-1.793z' fill='#607D8B'/><path d='M42.434 155.808c-2.51-.001-4.697-1.258-5.852-3.365-1.811-3.304-.438-7.634 3.059-9.654l12.291-7.098a7.599 7.599 0 0 1 3.789-1.033c2.51 0 4.697 1.258 5.852 3.365 1.811 3.304.439 7.634-3.059 9.654l-12.291 7.098a7.606 7.606 0 0 1-3.789 1.033zm13.287-20.683a7.128 7.128 0 0 0-3.555.971l-12.291 7.098c-3.279 1.893-4.573 5.942-2.883 9.024 1.071 1.955 3.106 3.122 5.442 3.122a7.13 7.13 0 0 0 3.556-.97l12.291-7.098c3.279-1.893 4.572-5.942 2.883-9.024-1.072-1.955-3.106-3.123-5.443-3.123z' fill='#607D8B'/><path d='M149.588 109.407l6.737 3.89v.887c0 .176.013.36.037.549.011.081.02.161.028.242.14 1.415.973 2.998 2.105 3.999l7.396 6.545c.177.156.358.295.541.415 1.579 1.04 2.95.466 3.062-1.282.049-.784.057-1.595.023-2.429l-.003-.16v-1.151l25.987 15.003v54c0 1.09-.77 1.53-1.72.982l-42.473-24.523c-.95-.548-1.72-1.877-1.72-2.966v-34.033' fill='#FAFAFA'/><path d='M194.553 191.25c-.257 0-.54-.085-.831-.253l-42.472-24.521c-.981-.567-1.779-1.943-1.779-3.068v-34.033h.234v34.033c0 1.051.745 2.336 1.661 2.866l42.473 24.521c.424.245.816.288 1.103.122.285-.164.442-.52.442-1.002v-53.933l-25.753-14.868.003 1.106c.034.832.026 1.654-.024 2.439-.054.844-.396 1.464-.963 1.746-.619.309-1.45.173-2.28-.373a5.023 5.023 0 0 1-.553-.426l-7.397-6.544c-1.158-1.026-1.999-2.625-2.143-4.076a9.624 9.624 0 0 0-.027-.238 4.241 4.241 0 0 1-.038-.564v-.82l-6.68-3.856.117-.202 6.738 3.89.058.034v.954c0 .171.012.351.036.533.011.083.021.165.029.246.138 1.395.948 2.935 2.065 3.923l7.397 6.545c.173.153.35.289.527.406.758.499 1.504.63 2.047.359.49-.243.786-.795.834-1.551.05-.778.057-1.591.024-2.417l-.004-.163v-1.355l.175.1 25.987 15.004.059.033v54.068c0 .569-.198.996-.559 1.204a1.002 1.002 0 0 1-.506.131' fill='#607D8B'/><path d='M145.685 163.161l24.115 13.922-25.978 14.998-1.462-.307c-6.534-2.17-13.628-3.728-21.019-4.616-4.365-.524-8.663 1.096-9.598 3.62a2.746 2.746 0 0 0-.011 1.928c1.538 4.267 4.236 8.363 7.995 12.135l.532.845-25.977 14.997-24.115-13.922 75.518-43.6' fill='#FFF'/><path d='M94.282 220.818l-.059-.033-24.29-14.024.175-.101 75.577-43.634.058.033 24.29 14.024-26.191 15.122-.045-.01-1.461-.307c-6.549-2.174-13.613-3.725-21.009-4.614a13.744 13.744 0 0 0-1.638-.097c-3.758 0-7.054 1.531-7.837 3.642a2.62 2.62 0 0 0-.01 1.848c1.535 4.258 4.216 8.326 7.968 12.091l.016.021.526.835.006.01.064.102-.105.061-25.977 14.998-.058.033zm-23.881-14.057l23.881 13.788 24.802-14.32c.546-.315.846-.489 1.017-.575l-.466-.74c-3.771-3.787-6.467-7.881-8.013-12.168a2.851 2.851 0 0 1 .011-2.008c.815-2.199 4.203-3.795 8.056-3.795.557 0 1.117.033 1.666.099 7.412.891 14.491 2.445 21.041 4.621.836.175 1.215.254 1.39.304l25.78-14.884-23.881-13.788-75.284 43.466z' fill='#607D8B'/><path d='M167.23 125.979v50.871l-27.321 15.773-6.461-14.167c-.91-1.996-3.428-1.738-5.624.574a10.238 10.238 0 0 0-2.33 4.018l-6.46 21.628-27.322 15.774v-50.871l75.518-43.6' fill='#FFF'/><path d='M91.712 220.567a.127.127 0 0 1-.059-.016.118.118 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.519-43.6a.117.117 0 0 1 .175.101v50.871c0 .041-.023.08-.059.1l-27.321 15.775a.118.118 0 0 1-.094.01.12.12 0 0 1-.071-.063l-6.46-14.168c-.375-.822-1.062-1.275-1.934-1.275-1.089 0-2.364.686-3.5 1.881a10.206 10.206 0 0 0-2.302 3.972l-6.46 21.627a.118.118 0 0 1-.054.068L91.77 220.551a.12.12 0 0 1-.058.016zm.117-50.92v50.601l27.106-15.65 6.447-21.583a10.286 10.286 0 0 1 2.357-4.065c1.18-1.242 2.517-1.954 3.669-1.954.969 0 1.731.501 2.146 1.411l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M168.543 126.213v50.87l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.624.574a10.248 10.248 0 0 0-2.33 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6' fill='#FFF'/><path d='M93.025 220.8a.123.123 0 0 1-.059-.015.12.12 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.518-43.6a.112.112 0 0 1 .117 0c.036.02.059.059.059.1v50.871a.116.116 0 0 1-.059.101l-27.321 15.774a.111.111 0 0 1-.094.01.115.115 0 0 1-.071-.062l-6.46-14.168c-.375-.823-1.062-1.275-1.935-1.275-1.088 0-2.363.685-3.499 1.881a10.19 10.19 0 0 0-2.302 3.971l-6.461 21.628a.108.108 0 0 1-.053.067l-27.322 15.775a.12.12 0 0 1-.058.015zm.117-50.919v50.6l27.106-15.649 6.447-21.584a10.293 10.293 0 0 1 2.357-4.065c1.179-1.241 2.516-1.954 3.668-1.954.969 0 1.732.502 2.147 1.412l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M169.8 177.083l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.625.574a10.246 10.246 0 0 0-2.329 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6v50.87z' fill='#FAFAFA'/><path d='M94.282 220.917a.234.234 0 0 1-.234-.233v-50.871c0-.083.045-.161.117-.202l75.518-43.601a.234.234 0 1 1 .35.202v50.871a.233.233 0 0 1-.116.202l-27.322 15.775a.232.232 0 0 1-.329-.106l-6.461-14.168c-.36-.789-.992-1.206-1.828-1.206-1.056 0-2.301.672-3.415 1.844a10.099 10.099 0 0 0-2.275 3.924l-6.46 21.628a.235.235 0 0 1-.107.136l-27.322 15.774a.23.23 0 0 1-.116.031zm.233-50.969v50.331l26.891-15.525 6.434-21.539a10.41 10.41 0 0 1 2.384-4.112c1.201-1.265 2.569-1.991 3.753-1.991 1.018 0 1.818.526 2.253 1.48l6.354 13.934 26.982-15.578v-50.331l-75.051 43.331z' fill='#607D8B'/><path d='M109.894 199.943c-1.774 0-3.241-.725-4.244-2.12a.224.224 0 0 1 .023-.294.233.233 0 0 1 .301-.023c.78.547 1.705.827 2.75.827 1.323 0 2.754-.439 4.256-1.306 5.311-3.067 9.631-10.518 9.631-16.611 0-1.927-.442-3.56-1.278-4.724a.232.232 0 0 1 .323-.327c1.671 1.172 2.591 3.381 2.591 6.219 0 6.242-4.426 13.863-9.865 17.003-1.574.908-3.084 1.356-4.488 1.356zm-2.969-1.542c.813.651 1.82.877 2.968.877h.001c1.321 0 2.753-.327 4.254-1.194 5.311-3.067 9.632-10.463 9.632-16.556 0-1.979-.463-3.599-1.326-4.761.411 1.035.625 2.275.625 3.635 0 6.243-4.426 13.883-9.865 17.023-1.574.909-3.084 1.317-4.49 1.317-.641 0-1.243-.149-1.799-.341z' fill='#607D8B'/><path d='M113.097 197.23c5.384-3.108 9.748-10.636 9.748-16.814 0-2.051-.483-3.692-1.323-4.86-1.784-1.252-4.374-1.194-7.257.47-5.384 3.108-9.748 10.636-9.748 16.814 0 2.051.483 3.692 1.323 4.86 1.784 1.252 4.374 1.194 7.257-.47' fill='#FAFAFA'/><path d='M108.724 198.614c-1.142 0-2.158-.213-3.019-.817-.021-.014-.04.014-.055-.007-.894-1.244-1.367-2.948-1.367-4.973 0-6.242 4.426-13.864 9.865-17.005 1.574-.908 3.084-1.363 4.49-1.363 1.142 0 2.158.309 3.018.913a.23.23 0 0 1 .056.056c.894 1.244 1.367 2.972 1.367 4.997 0 6.243-4.426 13.783-9.865 16.923-1.574.909-3.084 1.276-4.49 1.276zm-2.718-1.109c.774.532 1.688.776 2.718.776 1.323 0 2.754-.413 4.256-1.28 5.311-3.066 9.631-10.505 9.631-16.598 0-1.909-.434-3.523-1.255-4.685-.774-.533-1.688-.799-2.718-.799-1.323 0-2.755.441-4.256 1.308-5.311 3.066-9.631 10.506-9.631 16.599 0 1.909.434 3.517 1.255 4.679z' fill='#607D8B'/><path d='M149.318 114.262l-9.984 8.878 15.893 11.031 5.589-6.112-11.498-13.797' fill='#FAFAFA'/><path d='M169.676 120.84l-9.748 5.627c-3.642 2.103-9.528 2.113-13.147.024-3.62-2.089-3.601-5.488.041-7.591l9.495-5.608-6.729-3.885-81.836 47.071 45.923 26.514 3.081-1.779c.631-.365.869-.898.618-1.39-2.357-4.632-2.593-9.546-.683-14.262 5.638-13.92 24.509-24.815 48.618-28.07 8.169-1.103 16.68-.967 24.704.394.852.145 1.776.008 2.407-.357l3.081-1.778-25.825-14.91' fill='#FAFAFA'/><path d='M113.675 183.459a.47.47 0 0 1-.233-.062l-45.924-26.515a.468.468 0 0 1 .001-.809l81.836-47.071a.467.467 0 0 1 .466 0l6.729 3.885a.467.467 0 0 1-.467.809l-6.496-3.75-80.9 46.533 44.988 25.973 2.848-1.644c.192-.111.62-.409.435-.773-2.416-4.748-2.658-9.814-.7-14.65 2.806-6.927 8.885-13.242 17.582-18.263 8.657-4.998 19.518-8.489 31.407-10.094 8.198-1.107 16.79-.97 24.844.397.739.125 1.561.007 2.095-.301l2.381-1.374-25.125-14.506a.467.467 0 0 1 .467-.809l25.825 14.91a.467.467 0 0 1 0 .809l-3.081 1.779c-.721.417-1.763.575-2.718.413-7.963-1.351-16.457-1.486-24.563-.392-11.77 1.589-22.512 5.039-31.065 9.977-8.514 4.916-14.456 11.073-17.183 17.805-1.854 4.578-1.623 9.376.666 13.875.37.725.055 1.513-.8 2.006l-3.081 1.78a.476.476 0 0 1-.234.062' fill='#455A64'/><path d='M153.316 128.279c-2.413 0-4.821-.528-6.652-1.586-1.818-1.049-2.82-2.461-2.82-3.975 0-1.527 1.016-2.955 2.861-4.02l9.493-5.607a.233.233 0 1 1 .238.402l-9.496 5.609c-1.696.979-2.628 2.263-2.628 3.616 0 1.34.918 2.608 2.585 3.571 3.549 2.049 9.343 2.038 12.914-.024l9.748-5.628a.234.234 0 0 1 .234.405l-9.748 5.628c-1.858 1.072-4.296 1.609-6.729 1.609' fill='#607D8B'/><path d='M113.675 182.992l-45.913-26.508M113.675 183.342a.346.346 0 0 1-.175-.047l-45.913-26.508a.35.35 0 1 1 .35-.607l45.913 26.508a.35.35 0 0 1-.175.654' fill='#455A64'/><path d='M67.762 156.484v54.001c0 1.09.77 2.418 1.72 2.967l42.473 24.521c.95.549 1.72.11 1.72-.98v-54.001' fill='#FAFAFA'/><path d='M112.727 238.561c-.297 0-.62-.095-.947-.285l-42.473-24.521c-1.063-.613-1.895-2.05-1.895-3.27v-54.001a.35.35 0 1 1 .701 0v54.001c0 .96.707 2.18 1.544 2.663l42.473 24.522c.344.198.661.243.87.122.206-.119.325-.411.325-.799v-54.001a.35.35 0 1 1 .7 0v54.001c0 .655-.239 1.154-.675 1.406a1.235 1.235 0 0 1-.623.162' fill='#455A64'/><path d='M112.86 147.512h-.001c-2.318 0-4.499-.522-6.142-1.471-1.705-.984-2.643-2.315-2.643-3.749 0-1.445.952-2.791 2.68-3.788l12.041-6.953c1.668-.962 3.874-1.493 6.212-1.493 2.318 0 4.499.523 6.143 1.472 1.704.984 2.643 2.315 2.643 3.748 0 1.446-.952 2.791-2.68 3.789l-12.042 6.952c-1.668.963-3.874 1.493-6.211 1.493zm12.147-16.753c-2.217 0-4.298.497-5.861 1.399l-12.042 6.952c-1.502.868-2.33 1.998-2.33 3.182 0 1.173.815 2.289 2.293 3.142 1.538.889 3.596 1.378 5.792 1.378h.001c2.216 0 4.298-.497 5.861-1.399l12.041-6.953c1.502-.867 2.33-1.997 2.33-3.182 0-1.172-.814-2.288-2.292-3.142-1.539-.888-3.596-1.377-5.793-1.377z' fill='#607D8B'/><path d='M165.63 123.219l-5.734 3.311c-3.167 1.828-8.286 1.837-11.433.02-3.147-1.817-3.131-4.772.036-6.601l5.734-3.31 11.397 6.58' fill='#FAFAFA'/><path d='M154.233 117.448l9.995 5.771-4.682 2.704c-1.434.827-3.352 1.283-5.399 1.283-2.029 0-3.923-.449-5.333-1.263-1.29-.744-2-1.694-2-2.674 0-.991.723-1.955 2.036-2.713l5.383-3.108m0-.809l-5.734 3.31c-3.167 1.829-3.183 4.784-.036 6.601 1.568.905 3.623 1.357 5.684 1.357 2.077 0 4.159-.46 5.749-1.377l5.734-3.311-11.397-6.58M145.445 179.667c-1.773 0-3.241-.85-4.243-2.245-.067-.092-.057-.275.023-.356.08-.081.207-.12.3-.055.781.548 1.706.812 2.751.811 1.322 0 2.754-.446 4.256-1.313 5.31-3.066 9.631-10.522 9.631-16.615 0-1.927-.442-3.562-1.279-4.726a.235.235 0 0 1 .024-.301.232.232 0 0 1 .3-.027c1.67 1.172 2.59 3.38 2.59 6.219 0 6.242-4.425 13.987-9.865 17.127-1.573.908-3.083 1.481-4.488 1.481zM142.476 178c.814.651 1.82 1.002 2.969 1.002 1.322 0 2.753-.452 4.255-1.32 5.31-3.065 9.631-10.523 9.631-16.617 0-1.98-.463-3.63-1.325-4.793.411 1.035.624 2.26.624 3.62 0 6.242-4.425 13.875-9.865 17.015-1.573.909-3.084 1.376-4.489 1.376a5.49 5.49 0 0 1-1.8-.283z' fill='#607D8B'/><path d='M148.648 176.704c5.384-3.108 9.748-10.636 9.748-16.813 0-2.052-.483-3.693-1.322-4.861-1.785-1.252-4.375-1.194-7.258.471-5.383 3.108-9.748 10.636-9.748 16.813 0 2.051.484 3.692 1.323 4.86 1.785 1.253 4.374 1.195 7.257-.47' fill='#FAFAFA'/><path d='M144.276 178.276c-1.143 0-2.158-.307-3.019-.911a.217.217 0 0 1-.055-.054c-.895-1.244-1.367-2.972-1.367-4.997 0-6.241 4.425-13.875 9.865-17.016 1.573-.908 3.084-1.369 4.489-1.369 1.143 0 2.158.307 3.019.91a.24.24 0 0 1 .055.055c.894 1.244 1.367 2.971 1.367 4.997 0 6.241-4.425 13.875-9.865 17.016-1.573.908-3.084 1.369-4.489 1.369zm-2.718-1.172c.773.533 1.687.901 2.718.901 1.322 0 2.754-.538 4.256-1.405 5.31-3.066 9.631-10.567 9.631-16.661 0-1.908-.434-3.554-1.256-4.716-.774-.532-1.688-.814-2.718-.814-1.322 0-2.754.433-4.256 1.3-5.31 3.066-9.631 10.564-9.631 16.657 0 1.91.434 3.576 1.256 4.738z' fill='#607D8B'/><path d='M150.72 172.361l-.363-.295a24.105 24.105 0 0 0 2.148-3.128 24.05 24.05 0 0 0 1.977-4.375l.443.149a24.54 24.54 0 0 1-2.015 4.46 24.61 24.61 0 0 1-2.19 3.189M115.917 191.514l-.363-.294a24.174 24.174 0 0 0 2.148-3.128 24.038 24.038 0 0 0 1.976-4.375l.443.148a24.48 24.48 0 0 1-2.015 4.461 24.662 24.662 0 0 1-2.189 3.188M114 237.476V182.584 237.476' fill='#607D8B'/><g><path d='M81.822 37.474c.017-.135-.075-.28-.267-.392-.327-.188-.826-.21-1.109-.045l-6.012 3.471c-.131.076-.194.178-.191.285.002.132.002.461.002.578v.043l-.007.128-6.591 3.779c-.001 0-2.077 1.046-2.787 5.192 0 0-.912 6.961-.898 19.745.015 12.57.606 17.07 1.167 21.351.22 1.684 3.001 2.125 3.001 2.125.331.04.698-.027 1.08-.248l75.273-43.551c1.808-1.069 2.667-3.719 3.056-6.284 1.213-7.99 1.675-32.978-.275-39.878-.196-.693-.51-1.083-.868-1.282l-2.086-.79c-.727.028-1.416.467-1.534.535L82.032 37.072l-.21.402' fill='#FFF'/><path d='M144.311 1.701l2.085.79c.358.199.672.589.868 1.282 1.949 6.9 1.487 31.887.275 39.878-.39 2.565-1.249 5.215-3.056 6.284L69.21 93.486a1.78 1.78 0 0 1-.896.258l-.183-.011c0 .001-2.782-.44-3.003-2.124-.56-4.282-1.151-8.781-1.165-21.351-.015-12.784.897-19.745.897-19.745.71-4.146 2.787-5.192 2.787-5.192l6.591-3.779.007-.128v-.043c0-.117 0-.446-.002-.578-.003-.107.059-.21.191-.285l6.012-3.472a.98.98 0 0 1 .481-.11c.218 0 .449.053.627.156.193.112.285.258.268.392l.211-.402 60.744-34.836c.117-.068.806-.507 1.534-.535m0-.997l-.039.001c-.618.023-1.283.244-1.974.656l-.021.012-60.519 34.706a2.358 2.358 0 0 0-.831-.15c-.365 0-.704.084-.98.244l-6.012 3.471c-.442.255-.699.69-.689 1.166l.001.15-6.08 3.487c-.373.199-2.542 1.531-3.29 5.898l-.006.039c-.009.07-.92 7.173-.906 19.875.014 12.62.603 17.116 1.172 21.465l.002.015c.308 2.355 3.475 2.923 3.836 2.98l.034.004c.101.013.204.019.305.019a2.77 2.77 0 0 0 1.396-.392l75.273-43.552c1.811-1.071 2.999-3.423 3.542-6.997 1.186-7.814 1.734-33.096-.301-40.299-.253-.893-.704-1.527-1.343-1.882l-.132-.062-2.085-.789a.973.973 0 0 0-.353-.065' fill='#455A64'/><path d='M128.267 11.565l1.495.434-56.339 32.326' fill='#FFF'/><path d='M74.202 90.545a.5.5 0 0 1-.25-.931l18.437-10.645a.499.499 0 1 1 .499.864L74.451 90.478l-.249.067M75.764 42.654l-.108-.062.046-.171 5.135-2.964.17.045-.045.171-5.135 2.964-.063.017M70.52 90.375V46.421l.063-.036L137.84 7.554v43.954l-.062.036L70.52 90.375zm.25-43.811v43.38l66.821-38.579V7.985L70.77 46.564z' fill='#607D8B'/><path d='M86.986 83.182c-.23.149-.612.384-.849.523l-11.505 6.701c-.237.139-.206.252.068.252h.565c.275 0 .693-.113.93-.252L87.7 83.705c.237-.139.428-.253.425-.256a11.29 11.29 0 0 1-.006-.503c0-.274-.188-.377-.418-.227l-.715.463' fill='#607D8B'/><path d='M75.266 90.782H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.236-.138.615-.371.844-.519l.715-.464a.488.488 0 0 1 .266-.089c.172 0 .345.13.345.421 0 .214.001.363.003.437l.006.004-.004.069c-.003.075-.003.075-.486.356l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.108.866-.234l11.505-6.702c.168-.098.294-.173.361-.214-.004-.084-.004-.218-.004-.437l-.095-.171-.131.049-.714.463c-.232.15-.616.386-.854.525l-11.505 6.702-.029.018z' fill='#607D8B'/><path d='M75.266 89.871H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.258-.151.694-.268.993-.268h.565c.2 0 .316.056.346.166.03.11-.043.217-.215.317l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.107.866-.234l11.505-6.702.03-.018-.035-.001h-.565c-.252 0-.649.108-.867.234l-11.505 6.702-.029.018zM74.37 90.801v-1.247 1.247' fill='#607D8B'/><path d='M68.13 93.901c-.751-.093-1.314-.737-1.439-1.376-.831-4.238-1.151-8.782-1.165-21.352-.015-12.784.897-19.745.897-19.745.711-4.146 2.787-5.192 2.787-5.192l74.859-43.219c.223-.129 2.487-1.584 3.195.923 1.95 6.9 1.488 31.887.275 39.878-.389 2.565-1.248 5.215-3.056 6.283L69.21 93.653c-.382.221-.749.288-1.08.248 0 0-2.781-.441-3.001-2.125-.561-4.281-1.152-8.781-1.167-21.351-.014-12.784.898-19.745.898-19.745.71-4.146 2.787-5.191 2.787-5.191l6.598-3.81.871-.119 6.599-3.83.046-.461L68.13 93.901' fill='#FAFAFA'/><path d='M68.317 94.161l-.215-.013h-.001l-.244-.047c-.719-.156-2.772-.736-2.976-2.292-.568-4.34-1.154-8.813-1.168-21.384-.014-12.654.891-19.707.9-19.777.725-4.231 2.832-5.338 2.922-5.382l6.628-3.827.87-.119 6.446-3.742.034-.334a.248.248 0 0 1 .273-.223.248.248 0 0 1 .223.272l-.059.589-6.752 3.919-.87.118-6.556 3.785c-.031.016-1.99 1.068-2.666 5.018-.007.06-.908 7.086-.894 19.702.014 12.539.597 16.996 1.161 21.305.091.691.689 1.154 1.309 1.452a1.95 1.95 0 0 1-.236-.609c-.781-3.984-1.155-8.202-1.17-21.399-.014-12.653.891-19.707.9-19.777.725-4.231 2.832-5.337 2.922-5.382-.004.001 74.444-42.98 74.846-43.212l.028-.017c.904-.538 1.72-.688 2.36-.433.555.221.949.733 1.172 1.52 2.014 7.128 1.46 32.219.281 39.983-.507 3.341-1.575 5.515-3.175 6.462L69.335 93.869a2.023 2.023 0 0 1-1.018.292zm-.147-.507c.293.036.604-.037.915-.217l75.273-43.551c1.823-1.078 2.602-3.915 2.934-6.106 1.174-7.731 1.731-32.695-.268-39.772-.178-.631-.473-1.032-.876-1.192-.484-.193-1.166-.052-1.921.397l-.034.021-74.858 43.218c-.031.017-1.989 1.069-2.666 5.019-.007.059-.908 7.085-.894 19.702.015 13.155.386 17.351 1.161 21.303.09.461.476.983 1.037 1.139.114.025.185.037.196.039h.001z' fill='#455A64'/><path d='M69.317 68.982c.489-.281.885-.056.885.505 0 .56-.396 1.243-.885 1.525-.488.282-.884.057-.884-.504 0-.56.396-1.243.884-1.526' fill='#FFF'/><path d='M68.92 71.133c-.289 0-.487-.228-.487-.625 0-.56.396-1.243.884-1.526a.812.812 0 0 1 .397-.121c.289 0 .488.229.488.626 0 .56-.396 1.243-.885 1.525a.812.812 0 0 1-.397.121m.794-2.459a.976.976 0 0 0-.49.147c-.548.317-.978 1.058-.978 1.687 0 .486.271.812.674.812a.985.985 0 0 0 .491-.146c.548-.317.978-1.057.978-1.687 0-.486-.272-.813-.675-.813' fill='#8097A2'/><path d='M68.92 70.947c-.271 0-.299-.307-.299-.439 0-.491.361-1.116.79-1.363a.632.632 0 0 1 .303-.096c.272 0 .301.306.301.438 0 .491-.363 1.116-.791 1.364a.629.629 0 0 1-.304.096m.794-2.086a.812.812 0 0 0-.397.121c-.488.283-.884.966-.884 1.526 0 .397.198.625.487.625a.812.812 0 0 0 .397-.121c.489-.282.885-.965.885-1.525 0-.397-.199-.626-.488-.626' fill='#8097A2'/><path d='M69.444 85.35c.264-.152.477-.031.477.272 0 .303-.213.67-.477.822-.263.153-.477.031-.477-.271 0-.302.214-.671.477-.823' fill='#FFF'/><path d='M69.23 86.51c-.156 0-.263-.123-.263-.337 0-.302.214-.671.477-.823a.431.431 0 0 1 .214-.066c.156 0 .263.124.263.338 0 .303-.213.67-.477.822a.431.431 0 0 1-.214.066m.428-1.412c-.1 0-.203.029-.307.09-.32.185-.57.618-.57.985 0 .309.185.524.449.524a.63.63 0 0 0 .308-.09c.32-.185.57-.618.57-.985 0-.309-.185-.524-.45-.524' fill='#8097A2'/><path d='M69.23 86.322l-.076-.149c0-.235.179-.544.384-.661l.12-.041.076.151c0 .234-.179.542-.383.66l-.121.04m.428-1.038a.431.431 0 0 0-.214.066c-.263.152-.477.521-.477.823 0 .214.107.337.263.337a.431.431 0 0 0 .214-.066c.264-.152.477-.519.477-.822 0-.214-.107-.338-.263-.338' fill='#8097A2'/><path d='M139.278 7.769v43.667L72.208 90.16V46.493l67.07-38.724' fill='#455A64'/><path d='M72.083 90.375V46.421l.063-.036 67.257-38.831v43.954l-.062.036-67.258 38.831zm.25-43.811v43.38l66.821-38.579V7.985L72.333 46.564z' fill='#607D8B'/></g><path d='M125.737 88.647l-7.639 3.334V84l-11.459 4.713v8.269L99 100.315l13.369 3.646 13.368-15.314' fill='#455A64'/></g></svg>"; function RotateInstructions() { this.loadIcon_(); var overlay = document.createElement('div'); @@ -4030,7 +4428,7 @@ } }; RotateInstructions.prototype.loadIcon_ = function () { - this.icon = base64('image/svg+xml', rotateInstructionsAsset); + this.icon = dataUri('image/svg+xml', rotateInstructionsAsset); }; var DEFAULT_VIEWER = 'CardboardV1'; var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; @@ -4175,7 +4573,7 @@ }; var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {}; function unwrapExports$$1 (x) { - return x && x.__esModule ? x['default'] : x; + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } function createCommonjsModule$$1(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; @@ -4878,493 +5276,143 @@ }); var CardboardVRDisplay = unwrapExports(cardboardVrDisplay); -var PolyfilledXRDevice = function (_EventTarget) { - inherits(PolyfilledXRDevice, _EventTarget); - function PolyfilledXRDevice(global) { - classCallCheck(this, PolyfilledXRDevice); - var _this = possibleConstructorReturn(this, (PolyfilledXRDevice.__proto__ || Object.getPrototypeOf(PolyfilledXRDevice)).call(this)); - _this.global = global; - _this.onWindowResize = _this.onWindowResize.bind(_this); - _this.global.window.addEventListener('resize', _this.onWindowResize); - _this.environmentBlendMode = 'opaque'; - return _this; +class XRDevice extends EventTarget { + constructor(global) { + super(); + this.global = global; + this.onWindowResize = this.onWindowResize.bind(this); + this.global.window.addEventListener('resize', this.onWindowResize); + this.environmentBlendMode = 'opaque'; } - createClass(PolyfilledXRDevice, [{ - key: 'onBaseLayerSet', - value: function onBaseLayerSet(sessionId, layer) { - throw new Error('Not implemented'); - } - }, { - key: 'supportsSession', - value: function supportsSession() { - throw new Error('Not implemented'); - } - }, { - key: 'requestSession', - value: function requestSession() { - return new Promise(function ($return, $error) { - return $error(new Error('Not implemented')); - }.bind(this)); - } - }, { - key: 'requestAnimationFrame', - value: function requestAnimationFrame(callback) { - throw new Error('Not implemented'); - } - }, { - key: 'onFrameStart', - value: function onFrameStart(sessionId) { - throw new Error('Not implemented'); - } - }, { - key: 'onFrameEnd', - value: function onFrameEnd(sessionId) { - throw new Error('Not implemented'); - } - }, { - key: 'requestStageBounds', - value: function requestStageBounds() { - throw new Error('Not implemented'); - } - }, { - key: 'requestFrameOfReferenceTransform', - value: function requestFrameOfReferenceTransform(type, options) { - return new Promise(function ($return, $error) { - return $return(undefined); - }.bind(this)); - } - }, { - key: 'cancelAnimationFrame', - value: function cancelAnimationFrame(handle) { - throw new Error('Not implemented'); - } - }, { - key: 'endSession', - value: function endSession(sessionId) { - throw new Error('Not implemented'); - } - }, { - key: 'getViewport', - value: function getViewport(sessionId, eye, layer, target) { - throw new Error('Not implemented'); - } - }, { - key: 'getProjectionMatrix', - value: function getProjectionMatrix(eye) { - throw new Error('Not implemented'); - } - }, { - key: 'getBasePoseMatrix', - value: function getBasePoseMatrix() { - throw new Error('Not implemented'); - } - }, { - key: 'getBaseViewMatrix', - value: function getBaseViewMatrix(eye) { - throw new Error('Not implemented'); - } - }, { - key: 'getInputSources', - value: function getInputSources() { - throw new Error('Not implemented'); - } - }, { - key: 'getInputPose', - value: function getInputPose(inputSource, coordinateSystem) { - throw new Error('Not implemented'); - } - }, { - key: 'onWindowResize', - value: function onWindowResize() { - this.onWindowResize(); - } - }, { - key: 'depthNear', - get: function get$$1() { - throw new Error('Not implemented'); - } - , - set: function set$$1(val) { - throw new Error('Not implemented'); - } - }, { - key: 'depthFar', - get: function get$$1() { - throw new Error('Not implemented'); - } - , - set: function set$$1(val) { - throw new Error('Not implemented'); - } - }]); - return PolyfilledXRDevice; -}(EventTarget); - -function create$2() { - var out = new 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; -} - -function create$3() { - var out = new ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 0; - return out; -} -function clone$3(a) { - var out = new ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -} - -function copy$3(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -} - - - - - - - - - - - - - - - - - - -function normalize$1(out, a) { - var x = a[0]; - var y = a[1]; - var z = a[2]; - var w = a[3]; - var 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; + get depthNear() { throw new Error('Not implemented'); } + set depthNear(val) { throw new Error('Not implemented'); } + get depthFar() { throw new Error('Not implemented'); } + set depthFar(val) { throw new Error('Not implemented'); } + onBaseLayerSet(sessionId, layer) { throw new Error('Not implemented'); } + onInlineVerticalFieldOfViewSet(sessionId, value) { throw new Error('Not implemented'); } + supportsSession(mode) { throw new Error('Not implemented'); } + async requestSession(mode) { throw new Error('Not implemented'); } + requestAnimationFrame(callback) { throw new Error('Not implemented'); } + onFrameStart(sessionId) { throw new Error('Not implemented'); } + onFrameEnd(sessionId) { throw new Error('Not implemented'); } + requestStageBounds() { throw new Error('Not implemented'); } + async requestFrameOfReferenceTransform(type, options) { + return undefined; } - return out; -} - - - - - - - - - - - - - - - -var forEach$1 = function () { - var vec = create$3(); - return function (a, stride, offset, count, fn, arg) { - var i = void 0, - l = void 0; - 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; - }; -}(); - -function create$4() { - var out = new ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -} - -function setAxisAngle(out, axis, rad) { - rad = rad * 0.5; - var 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; -} - -function multiply$4(out, a, b) { - var ax = a[0], - ay = a[1], - az = a[2], - aw = a[3]; - var 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; -} - - - - -function slerp(out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2], - aw = a[3]; - var bx = b[0], - by = b[1], - bz = b[2], - bw = b[3]; - var omega = void 0, - cosom = void 0, - sinom = void 0, - scale0 = void 0, - scale1 = void 0; - cosom = ax * bx + ay * by + az * bz + aw * bw; - if (cosom < 0.0) { - cosom = -cosom; - bx = -bx; - by = -by; - bz = -bz; - bw = -bw; + cancelAnimationFrame(handle) { throw new Error('Not implemented'); } + endSession(sessionId) { throw new Error('Not implemented'); } + getViewport(sessionId, eye, layer, target) { throw new Error('Not implemented'); } + getProjectionMatrix(eye) { throw new Error('Not implemented'); } + getBasePoseMatrix() { throw new Error('Not implemented'); } + getBaseViewMatrix(eye) { throw new Error('Not implemented'); } + getInputSources() { throw new Error('Not implemented'); } + getInputPose(inputSource, coordinateSystem, poseType) { throw new Error('Not implemented'); } + onWindowResize() { + this.onWindowResize(); } - if (1.0 - cosom > 0.000001) { - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - scale0 = 1.0 - t; - scale1 = t; +} + +let oculusTouch = { + mapping: 'xr-standard', + id: 'oculus-touch', + buttons: { + length: 6, + 0: 1, + 1: 0, + 2: 2, + 3: null, + 4: 3, + 5: 4 + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] } - 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; -} -function invert$2(out, a) { - var a0 = a[0], - a1 = a[1], - a2 = a[2], - a3 = a[3]; - var dot$$1 = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; - var invDot = dot$$1 ? 1.0 / dot$$1 : 0; - out[0] = -a0 * invDot; - out[1] = -a1 * invDot; - out[2] = -a2 * invDot; - out[3] = a3 * invDot; - return out; -} - -function fromMat3(out, m) { - var fTrace = m[0] + m[4] + m[8]; - var fRoot = void 0; - if (fTrace > 0.0) { - fRoot = Math.sqrt(fTrace + 1.0); - out[3] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; - out[0] = (m[5] - m[7]) * fRoot; - out[1] = (m[6] - m[2]) * fRoot; - out[2] = (m[1] - m[3]) * fRoot; - } else { - var i = 0; - if (m[4] > m[0]) i = 1; - if (m[8] > m[i * 3 + i]) i = 2; - var j = (i + 1) % 3; - var 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; +}; +let windowsMixedReality = { + mapping: 'xr-standard', + id: 'windows-mixed-reality', + buttons: { + length: 4, + 0: 1, + 1: 0, + 2: 2, + 3: 4, + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] } - return out; -} -function fromEuler(out, x, y, z) { - var halfToRad = 0.5 * Math.PI / 180.0; - x *= halfToRad; - y *= halfToRad; - z *= halfToRad; - var sx = Math.sin(x); - var cx = Math.cos(x); - var sy = Math.sin(y); - var cy = Math.cos(y); - var sz = Math.sin(z); - var 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; -} - -var clone$4 = clone$3; - -var copy$4 = copy$3; - - - - - - - - - - -var normalize$2 = normalize$1; - - -var rotationTo = function () { - var tmpvec3 = create$1(); - var xUnitVec3 = fromValues$1(1, 0, 0); - var yUnitVec3 = fromValues$1(0, 1, 0); - return function (out, a, b) { - var dot$$1 = dot(a, b); - if (dot$$1 < -0.999999) { - cross(tmpvec3, xUnitVec3, a); - if (len(tmpvec3) < 0.000001) cross(tmpvec3, yUnitVec3, a); - normalize(tmpvec3, tmpvec3); - setAxisAngle(out, tmpvec3, Math.PI); - return out; - } else if (dot$$1 > 0.999999) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; - } else { - cross(tmpvec3, a, b); - out[0] = tmpvec3[0]; - out[1] = tmpvec3[1]; - out[2] = tmpvec3[2]; - out[3] = 1 + dot$$1; - return normalize$2(out, out); +}; +let GamepadMappings = { + "Oculus Touch (Right)": oculusTouch, + "Oculus Touch (Left)": oculusTouch, + "Oculus Go Controller": { + mapping: 'xr-standard', + id: 'oculus-go', + buttons: { + 0: 1, + 1: 0, + }, + gripTransform: { + orientation: [Math.PI * 0.11, 0, 0, 1] } - }; -}(); -var sqlerp = function () { - var temp1 = create$4(); - var temp2 = create$4(); - 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; - }; -}(); -var setAxes = function () { - var matr = create$2(); - 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$2(out, fromMat3(out, matr)); - }; -}(); + }, + "Windows Mixed Reality (Right)": windowsMixedReality, + "Windows Mixed Reality (Left)": windowsMixedReality, +}; -var HEAD_ELBOW_OFFSET_RIGHTHANDED = fromValues$1(0.155, -0.465, -0.15); -var HEAD_ELBOW_OFFSET_LEFTHANDED = fromValues$1(-0.155, -0.465, -0.15); -var ELBOW_WRIST_OFFSET = fromValues$1(0, 0, -0.25); -var WRIST_CONTROLLER_OFFSET = fromValues$1(0, 0, 0.05); -var ARM_EXTENSION_OFFSET = fromValues$1(-0.08, 0.14, 0.08); -var ELBOW_BEND_RATIO = 0.4; -var EXTENSION_RATIO_WEIGHT = 0.4; -var MIN_ANGULAR_SPEED = 0.61; -var MIN_ANGLE_DELTA = 0.175; -var MIN_EXTENSION_COS = 0.12; -var MAX_EXTENSION_COS = 0.87; -var RAD_TO_DEG = 180 / Math.PI; +const HEAD_ELBOW_OFFSET_RIGHTHANDED = fromValues$1(0.155, -0.465, -0.15); +const HEAD_ELBOW_OFFSET_LEFTHANDED = fromValues$1(-0.155, -0.465, -0.15); +const ELBOW_WRIST_OFFSET = fromValues$1(0, 0, -0.25); +const WRIST_CONTROLLER_OFFSET = fromValues$1(0, 0, 0.05); +const ARM_EXTENSION_OFFSET = fromValues$1(-0.08, 0.14, 0.08); +const ELBOW_BEND_RATIO = 0.4; +const EXTENSION_RATIO_WEIGHT = 0.4; +const MIN_ANGULAR_SPEED = 0.61; +const MIN_ANGLE_DELTA = 0.175; +const MIN_EXTENSION_COS = 0.12; +const MAX_EXTENSION_COS = 0.87; +const RAD_TO_DEG = 180 / Math.PI; function eulerFromQuaternion(out, q, order) { function clamp(value, min$$1, max$$1) { - return value < min$$1 ? min$$1 : value > max$$1 ? max$$1 : value; + return (value < min$$1 ? min$$1 : (value > max$$1 ? max$$1 : value)); } var sqx = q[0] * q[0]; var sqy = q[1] * q[1]; var sqz = q[2] * q[2]; var sqw = q[3] * q[3]; - if (order === 'XYZ') { - out[0] = Math.atan2(2 * (q[0] * q[3] - q[1] * q[2]), sqw - sqx - sqy + sqz); - out[1] = Math.asin(clamp(2 * (q[0] * q[2] + q[1] * q[3]), -1, 1)); - out[2] = Math.atan2(2 * (q[2] * q[3] - q[0] * q[1]), sqw + sqx - sqy - sqz); - } else if (order === 'YXZ') { - out[0] = Math.asin(clamp(2 * (q[0] * q[3] - q[1] * q[2]), -1, 1)); - out[1] = Math.atan2(2 * (q[0] * q[2] + q[1] * q[3]), sqw - sqx - sqy + sqz); - out[2] = Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), sqw - sqx + sqy - sqz); - } else if (order === 'ZXY') { - out[0] = Math.asin(clamp(2 * (q[0] * q[3] + q[1] * q[2]), -1, 1)); - out[1] = Math.atan2(2 * (q[1] * q[3] - q[2] * q[0]), sqw - sqx - sqy + sqz); - out[2] = Math.atan2(2 * (q[2] * q[3] - q[0] * q[1]), sqw - sqx + sqy - sqz); - } else if (order === 'ZYX') { - out[0] = Math.atan2(2 * (q[0] * q[3] + q[2] * q[1]), sqw - sqx - sqy + sqz); - out[1] = Math.asin(clamp(2 * (q[1] * q[3] - q[0] * q[2]), -1, 1)); - out[2] = Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), sqw + sqx - sqy - sqz); - } else if (order === 'YZX') { - out[0] = Math.atan2(2 * (q[0] * q[3] - q[2] * q[1]), sqw - sqx + sqy - sqz); - out[1] = Math.atan2(2 * (q[1] * q[3] - q[0] * q[2]), sqw + sqx - sqy - sqz); - out[2] = Math.asin(clamp(2 * (q[0] * q[1] + q[2] * q[3]), -1, 1)); - } else if (order === 'XZY') { - out[0] = Math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), sqw - sqx + sqy - sqz); - out[1] = Math.atan2(2 * (q[0] * q[2] + q[1] * q[3]), sqw + sqx - sqy - sqz); - out[2] = Math.asin(clamp(2 * (q[2] * q[3] - q[0] * q[1]), -1, 1)); + if ( order === 'XYZ' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[1] * q[2] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[0] * q[2] + q[1] * q[3] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YXZ' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] - q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZXY' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] + q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[2] * q[0] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZYX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[2] * q[1] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[1] * q[3] - q[0] * q[2] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YZX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[2] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[0] * q[2] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[0] * q[1] + q[2] * q[3] ), -1, 1 ) ); + } else if ( order === 'XZY' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[1] * q[2] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[2] * q[3] - q[0] * q[1] ), -1, 1 ) ); } else { console.log('No order given for quaternion to euler conversion.'); return; } } -var OrientationArmModel = function () { - function OrientationArmModel() { - classCallCheck(this, OrientationArmModel); +class OrientationArmModel { + constructor() { this.hand = 'right'; this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; this.controllerQ = create$4(); @@ -5378,115 +5426,196 @@ this.rootQ = create$4(); this.position = create$1(); } - createClass(OrientationArmModel, [{ - key: 'setHandedness', - value: function setHandedness(hand) { - if (this.hand != hand) { - this.hand = hand; - if (this.hand == 'left') { - this.headElbowOffset = HEAD_ELBOW_OFFSET_LEFTHANDED; - } else { - this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; - } - } - } - }, { - key: 'update', - value: function update(controllerOrientation, headPoseMatrix) { - this.time = now$1(); - if (controllerOrientation) { - copy$4(this.lastControllerQ, this.controllerQ); - copy$4(this.controllerQ, controllerOrientation); - } - if (headPoseMatrix) { - getTranslation(this.headPos, headPoseMatrix); - getRotation(this.headQ, headPoseMatrix); - } - var headYawQ = this.getHeadYawOrientation_(); - var angleDelta = this.quatAngle_(this.lastControllerQ, this.controllerQ); - var timeDelta = (this.time - this.lastTime) / 1000; - var controllerAngularSpeed = angleDelta / timeDelta; - if (controllerAngularSpeed > MIN_ANGULAR_SPEED) { - slerp(this.rootQ, this.rootQ, headYawQ, Math.min(angleDelta / MIN_ANGLE_DELTA, 1.0)); + setHandedness(hand) { + if (this.hand != hand) { + this.hand = hand; + if (this.hand == 'left') { + this.headElbowOffset = HEAD_ELBOW_OFFSET_LEFTHANDED; } else { - copy$4(this.rootQ, headYawQ); + this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; } - var controllerForward = fromValues$1(0, 0, -1.0); - transformQuat(controllerForward, controllerForward, this.controllerQ); - var controllerDotY = dot(controllerForward, [0, 1, 0]); - var extensionRatio = this.clamp_((controllerDotY - MIN_EXTENSION_COS) / MAX_EXTENSION_COS, 0.0, 1.0); - var controllerCameraQ = clone$4(this.rootQ); - invert$2(controllerCameraQ, controllerCameraQ); - multiply$4(controllerCameraQ, controllerCameraQ, this.controllerQ); - var elbowPos = this.elbowPos; - copy$1(elbowPos, this.headPos); - add$1(elbowPos, elbowPos, this.headElbowOffset); - var elbowOffset = clone$1(ARM_EXTENSION_OFFSET); - scale$1(elbowOffset, elbowOffset, extensionRatio); - add$1(elbowPos, elbowPos, elbowOffset); - var totalAngle = this.quatAngle_(controllerCameraQ, create$4()); - var totalAngleDeg = totalAngle * RAD_TO_DEG; - var lerpSuppression = 1 - Math.pow(totalAngleDeg / 180, 4);var elbowRatio = ELBOW_BEND_RATIO; - var wristRatio = 1 - ELBOW_BEND_RATIO; - var lerpValue = lerpSuppression * (elbowRatio + wristRatio * extensionRatio * EXTENSION_RATIO_WEIGHT); - var wristQ = create$4(); - slerp(wristQ, wristQ, controllerCameraQ, lerpValue); - var invWristQ = invert$2(create$4(), wristQ); - var elbowQ = clone$4(controllerCameraQ); - multiply$4(elbowQ, elbowQ, invWristQ); - var wristPos = this.wristPos; - copy$1(wristPos, WRIST_CONTROLLER_OFFSET); - transformQuat(wristPos, wristPos, wristQ); - add$1(wristPos, wristPos, ELBOW_WRIST_OFFSET); - transformQuat(wristPos, wristPos, elbowQ); - add$1(wristPos, wristPos, elbowPos); - var offset = clone$1(ARM_EXTENSION_OFFSET); - scale$1(offset, offset, extensionRatio); - add$1(this.position, this.wristPos, offset); - transformQuat(this.position, this.position, this.rootQ); - this.lastTime = this.time; } - }, { - key: 'getPosition', - value: function getPosition() { - return this.position; + } + update(controllerOrientation, headPoseMatrix) { + this.time = now$1(); + if (controllerOrientation) { + copy$4(this.lastControllerQ, this.controllerQ); + copy$4(this.controllerQ, controllerOrientation); } - }, { - key: 'getHeadYawOrientation_', - value: function getHeadYawOrientation_() { - var headEuler = create$1(); - eulerFromQuaternion(headEuler, this.headQ, 'YXZ'); - var destinationQ = fromEuler(create$4(), 0, headEuler[1] * RAD_TO_DEG, 0); - return destinationQ; + if (headPoseMatrix) { + getTranslation(this.headPos, headPoseMatrix); + getRotation(this.headQ, headPoseMatrix); } - }, { - key: 'clamp_', - value: function clamp_(value, min$$1, max$$1) { - return Math.min(Math.max(value, min$$1), max$$1); + let headYawQ = this.getHeadYawOrientation_(); + let angleDelta = this.quatAngle_(this.lastControllerQ, this.controllerQ); + let timeDelta = (this.time - this.lastTime) / 1000; + let controllerAngularSpeed = angleDelta / timeDelta; + if (controllerAngularSpeed > MIN_ANGULAR_SPEED) { + slerp(this.rootQ, this.rootQ, headYawQ, + Math.min(angleDelta / MIN_ANGLE_DELTA, 1.0)); + } else { + copy$4(this.rootQ, headYawQ); } - }, { - key: 'quatAngle_', - value: function quatAngle_(q1, q2) { - var vec1 = [0, 0, -1]; - var vec2 = [0, 0, -1]; - transformQuat(vec1, vec1, q1); - transformQuat(vec2, vec2, q2); - return angle(vec1, vec2); - } - }]); - return OrientationArmModel; -}(); + let controllerForward = fromValues$1(0, 0, -1.0); + transformQuat(controllerForward, controllerForward, this.controllerQ); + let controllerDotY = dot(controllerForward, [0, 1, 0]); + let extensionRatio = this.clamp_( + (controllerDotY - MIN_EXTENSION_COS) / MAX_EXTENSION_COS, 0.0, 1.0); + let controllerCameraQ = clone$4(this.rootQ); + invert$2(controllerCameraQ, controllerCameraQ); + multiply$4(controllerCameraQ, controllerCameraQ, this.controllerQ); + let elbowPos = this.elbowPos; + copy$1(elbowPos, this.headPos); + add$1(elbowPos, elbowPos, this.headElbowOffset); + let elbowOffset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(elbowOffset, elbowOffset, extensionRatio); + add$1(elbowPos, elbowPos, elbowOffset); + let totalAngle = this.quatAngle_(controllerCameraQ, create$4()); + let totalAngleDeg = totalAngle * RAD_TO_DEG; + let lerpSuppression = 1 - Math.pow(totalAngleDeg / 180, 4);let elbowRatio = ELBOW_BEND_RATIO; + let wristRatio = 1 - ELBOW_BEND_RATIO; + let lerpValue = lerpSuppression * + (elbowRatio + wristRatio * extensionRatio * EXTENSION_RATIO_WEIGHT); + let wristQ = create$4(); + slerp(wristQ, wristQ, controllerCameraQ, lerpValue); + let invWristQ = invert$2(create$4(), wristQ); + let elbowQ = clone$4(controllerCameraQ); + multiply$4(elbowQ, elbowQ, invWristQ); + let wristPos = this.wristPos; + copy$1(wristPos, WRIST_CONTROLLER_OFFSET); + transformQuat(wristPos, wristPos, wristQ); + add$1(wristPos, wristPos, ELBOW_WRIST_OFFSET); + transformQuat(wristPos, wristPos, elbowQ); + add$1(wristPos, wristPos, elbowPos); + let offset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(offset, offset, extensionRatio); + add$1(this.position, this.wristPos, offset); + transformQuat(this.position, this.position, this.rootQ); + this.lastTime = this.time; + } + getPosition() { + return this.position; + } + getHeadYawOrientation_() { + let headEuler = create$1(); + eulerFromQuaternion(headEuler, this.headQ, 'YXZ'); + let destinationQ = fromEuler(create$4(), 0, headEuler[1] * RAD_TO_DEG, 0); + return destinationQ; + } + clamp_(value, min$$1, max$$1) { + return Math.min(Math.max(value, min$$1), max$$1); + } + quatAngle_(q1, q2) { + let vec1 = [0, 0, -1]; + let vec2 = [0, 0, -1]; + transformQuat(vec1, vec1, q1); + transformQuat(vec2, vec2, q2); + return angle(vec1, vec2); + } +} -var GamepadXRInputSource = function () { - function GamepadXRInputSource(polyfill) { - var primaryButtonIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - classCallCheck(this, GamepadXRInputSource); +const PRIVATE$16 = Symbol('@@webxr-polyfill/XRRemappedGamepad'); +const PLACEHOLDER_BUTTON = { pressed: false, touched: false, value: 0.0 }; +Object.freeze(PLACEHOLDER_BUTTON); +class XRRemappedGamepad { + constructor(gamepad, map) { + if (!map) { + map = {}; + } + let axes = new Array(map.axes ? map.axes.length : gamepad.axes.length); + let buttons = new Array(map.buttons ? map.buttons.length : gamepad.buttons.length); + let gripTransform = null; + if (map.gripTransform) { + let orientation = map.gripTransform.orientation || [0, 0, 0, 1]; + gripTransform = create(); + fromRotationTranslation( + gripTransform, + normalize$2(orientation, orientation), + map.gripTransform.position || [0, 0, 0] + ); + } + let targetRayTransform = null; + if (map.targetRayTransform) { + let orientation = map.targetRayTransform.orientation || [0, 0, 0, 1]; + targetRayTransform = create(); + fromRotationTranslation( + targetRayTransform, + normalize$2(orientation, orientation), + map.targetRayTransform.position || [0, 0, 0] + ); + } + this[PRIVATE$16] = { + gamepad, + map, + id: map.id || gamepad.id, + mapping: map.mapping || gamepad.mapping, + axes, + buttons, + gripTransform, + targetRayTransform, + }; + this._update(); + } + _update() { + let gamepad = this[PRIVATE$16].gamepad; + let map = this[PRIVATE$16].map; + let axes = this[PRIVATE$16].axes; + for (let i = 0; i < axes.length; ++i) { + if (map.axes && i in map.axes) { + if (map.axes[i] === null) { + axes[i] = 0; + } else { + axes[i] = gamepad.axes[map.axes[i]]; + } + } else { + axes[i] = gamepad.axes[i]; + } + } + let buttons = this[PRIVATE$16].buttons; + for (let i = 0; i < buttons.length; ++i) { + if (map.buttons && i in map.buttons) { + if (map.buttons[i] === null) { + buttons[i] = PLACEHOLDER_BUTTON; + } else { + buttons[i] = gamepad.buttons[map.buttons[i]]; + } + } else { + buttons[i] = gamepad.buttons[i]; + } + } + } + get id() { + return this[PRIVATE$16].id; + } + get index() { + return 0; + } + get connected() { + return this[PRIVATE$16].gamepad.connected; + } + get timestamp() { + return this[PRIVATE$16].gamepad.timestamp; + } + get mapping() { + return this[PRIVATE$16].mapping; + } + get axes() { + return this[PRIVATE$16].axes; + } + get buttons() { + return this[PRIVATE$16].buttons; + } +} +class GamepadXRInputSource { + constructor(polyfill, primaryButtonIndex = 0) { this.polyfill = polyfill; + this.nativeGamepad = null; this.gamepad = null; this.inputSource = new XRInputSource(this); this.lastPosition = create$1(); this.emulatedPosition = false; this.basePoseMatrix = create(); + this.outputMatrix = create(); this.inputPoses = new WeakMap(); this.primaryButtonIndex = primaryButtonIndex; this.primaryActionPressed = false; @@ -5494,675 +5623,514 @@ this.targetRayMode = 'gaze'; this.armModel = null; } - createClass(GamepadXRInputSource, [{ - key: 'updateFromGamepad', - value: function updateFromGamepad(gamepad) { - this.gamepad = gamepad; - this.handedness = gamepad.hand; - if (gamepad.pose) { - this.targetRayMode = 'tracked-pointer'; - this.emulatedPosition = !gamepad.pose.hasPosition; - } else if (gamepad.hand === '') { - this.targetRayMode = 'gaze'; - this.emulatedPosition = false; - } - } - }, { - key: 'updateBasePoseMatrix', - value: function updateBasePoseMatrix() { - if (this.gamepad && this.gamepad.pose) { - var pose = this.gamepad.pose; - var position = pose.position; - var orientation = pose.orientation; - if (!position && !orientation) { - return; - } - if (!position) { - if (!pose.hasPosition) { - if (!this.armModel) { - this.armModel = new OrientationArmModel(); - } - this.armModel.setHandedness(this.gamepad.hand); - this.armModel.update(orientation, this.polyfill.getBasePoseMatrix()); - position = this.armModel.getPosition(); - } else { - position = this.lastPosition; - } - } else { - this.lastPosition[0] = position[0]; - this.lastPosition[1] = position[1]; - this.lastPosition[2] = position[2]; - } - fromRotationTranslation(this.basePoseMatrix, orientation, position); + updateFromGamepad(gamepad) { + if (this.nativeGamepad !== gamepad) { + this.nativeGamepad = gamepad; + if (gamepad) { + this.gamepad = new XRRemappedGamepad(gamepad, GamepadMappings[gamepad.id]); } else { - copy(this.basePoseMatrix, this.polyfill.getBasePoseMatrix()); + this.gamepad = null; } - return this.basePoseMatrix; } - }, { - key: 'getXRInputPose', - value: function getXRInputPose(coordinateSystem) { - this.updateBasePoseMatrix(); - var inputPose = this.inputPoses.get(coordinateSystem); - if (!inputPose) { - inputPose = new XRInputPose(this, this.gamepad && this.gamepad.pose); - this.inputPoses.set(coordinateSystem, inputPose); - } - var rayTransformMatrix = new Float32Array(16); - coordinateSystem.transformBasePoseMatrix(rayTransformMatrix, this.basePoseMatrix); - inputPose.targetRay = poseMatrixToXRRay(rayTransformMatrix); - if (inputPose.gripMatrix) { - coordinateSystem.transformBasePoseMatrix(inputPose.gripMatrix, this.basePoseMatrix); - } - return inputPose; + this.handedness = gamepad.hand; + if (this.gamepad) { + this.gamepad._update(); } - }]); - return GamepadXRInputSource; -}(); - -var EXTRA_PRESENTATION_ATTRIBUTES = { - highRefreshRate: true -}; -var PRIMARY_BUTTON_MAP = { - oculus: 1, - openvr: 1 -}; -var CAN_USE_GAMEPAD = _global.navigator && 'getGamepads' in _global.navigator; -var SESSION_ID = 0; -var Session = function Session(sessionOptions) { - classCallCheck(this, Session); - this.outputContext = sessionOptions.outputContext; - this.immersive = sessionOptions.immersive; - this.ended = null; - this.baseLayer = null; - this.id = ++SESSION_ID; - this.modifiedCanvasLayer = false; -}; - -var WebVRDevice = function (_PolyfilledXRDevice) { - inherits(WebVRDevice, _PolyfilledXRDevice); - function WebVRDevice(global, display) { - classCallCheck(this, WebVRDevice); - var canPresent = display.capabilities.canPresent; - var _this = possibleConstructorReturn(this, (WebVRDevice.__proto__ || Object.getPrototypeOf(WebVRDevice)).call(this, global)); - _this.display = display; - _this.frame = new global.VRFrameData(); - _this.sessions = new Map(); - _this.immersiveSession = null; - _this.canPresent = canPresent; - _this.baseModelMatrix = create(); - _this.gamepadInputSources = {}; - _this.tempVec3 = new Float32Array(3); - _this.onVRDisplayPresentChange = _this.onVRDisplayPresentChange.bind(_this); - global.window.addEventListener('vrdisplaypresentchange', _this.onVRDisplayPresentChange); - return _this; + if (gamepad.pose) { + this.targetRayMode = 'tracked-pointer'; + this.emulatedPosition = !gamepad.pose.hasPosition; + } else if (gamepad.hand === '') { + this.targetRayMode = 'gaze'; + this.emulatedPosition = false; + } } - createClass(WebVRDevice, [{ - key: 'onBaseLayerSet', - value: function onBaseLayerSet(sessionId, layer) { - var _this2 = this; - var session = this.sessions.get(sessionId); - var canvas = layer.context.canvas; - if (session.immersive) { - var left = this.display.getEyeParameters('left'); - var right = this.display.getEyeParameters('right'); - canvas.width = Math.max(left.renderWidth, right.renderWidth) * 2; - canvas.height = Math.max(left.renderHeight, right.renderHeight); - this.display.requestPresent([{ - source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES - }]).then(function () { - if ("production" !== 'test' && !_this2.global.document.body.contains(canvas)) { - session.modifiedCanvasLayer = true; - _this2.global.document.body.appendChild(canvas); - applyCanvasStylesForMinimalRendering(canvas); - } - session.baseLayer = layer; - }); - } - else if (session.outputContext) { - session.baseLayer = layer; - } - } - }, { - key: 'supportsSession', - value: function supportsSession() { - var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - if (options.immersive === true && this.canPresent === false) { - return false; - } - return true; - } - }, { - key: 'requestSession', - value: function requestSession() { - var $args = arguments;return new Promise(function ($return, $error) { - var options, canvas, ctx, session; - options = $args.length > 0 && $args[0] !== undefined ? $args[0] : {}; - if (!this.supportsSession(options)) { - return $return(Promise.reject()); - } - if (options.immersive) { - canvas = this.global.document.createElement('canvas'); - { - ctx = canvas.getContext('webgl'); - } - return Promise.resolve(this.display.requestPresent([{ - source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES }])).then(function ($await_2) { - try { - return $If_1.call(this); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this), $error); - } - function $If_1() { - session = new Session(options); - this.sessions.set(session.id, session); - if (options.immersive) { - this.immersiveSession = session; - this.dispatchEvent('@@webxr-polyfill/vr-present-start', session.id); - } - return $return(Promise.resolve(session.id)); - } - return $If_1.call(this); - }.bind(this)); - } - }, { - key: 'requestAnimationFrame', - value: function requestAnimationFrame(callback) { - return this.display.requestAnimationFrame(callback); - } - }, { - key: 'getPrimaryButtonIndex', - value: function getPrimaryButtonIndex(gamepad) { - var primaryButton = 0; - var name = gamepad.id.toLowerCase(); - for (var key in PRIMARY_BUTTON_MAP) { - if (name.includes(key)) { - primaryButton = PRIMARY_BUTTON_MAP[key]; - break; - } - } - return Math.min(primaryButton, gamepad.buttons.length - 1); - } - }, { - key: 'onFrameStart', - value: function onFrameStart(sessionId) { - this.display.getFrameData(this.frame); - var session = this.sessions.get(sessionId); - if (session.immersive && CAN_USE_GAMEPAD) { - var prevInputSources = this.gamepadInputSources; - this.gamepadInputSources = {}; - var gamepads = _global.navigator.getGamepads(); - for (var i = 0; i < gamepads.length; ++i) { - var gamepad = gamepads[i]; - if (gamepad && gamepad.displayId === this.display.displayId) { - var inputSourceImpl = prevInputSources[i]; - if (!inputSourceImpl) { - inputSourceImpl = new GamepadXRInputSource(this, this.getPrimaryButtonIndex(gamepad)); - } - inputSourceImpl.updateFromGamepad(gamepad); - this.gamepadInputSources[i] = inputSourceImpl; - if (inputSourceImpl.primaryButtonIndex != -1) { - var primaryActionPressed = gamepad.buttons[inputSourceImpl.primaryButtonIndex].pressed; - if (primaryActionPressed && !inputSourceImpl.primaryActionPressed) { - this.dispatchEvent('@@webxr-polyfill/input-select-start', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); - } else if (!primaryActionPressed && inputSourceImpl.primaryActionPressed) { - this.dispatchEvent('@@webxr-polyfill/input-select-end', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); - } - inputSourceImpl.primaryActionPressed = primaryActionPressed; - } - } - } - } - if (session.outputContext && !session.immersive) { - var outputCanvas = session.outputContext.canvas; - var oWidth = outputCanvas.offsetWidth; - var oHeight = outputCanvas.offsetHeight; - if (outputCanvas.width != oWidth) { - outputCanvas.width = oWidth; - } - if (outputCanvas.height != oHeight) { - outputCanvas.height = oHeight; - } - var canvas = session.baseLayer.context.canvas; - if (!this.immersiveSession || canvas !== this.immersiveSession.baseLayer.context.canvas) { - if (canvas.width != oWidth) { - canvas.width = oWidth; - } - if (canvas.height != oHeight) { - canvas.height = oHeight; - } - perspective(this.frame.leftProjectionMatrix, Math.PI * 0.4, oWidth / oHeight, this.depthNear, this.depthFar); - } - } - } - }, { - key: 'onFrameEnd', - value: function onFrameEnd(sessionId) { - var session = this.sessions.get(sessionId); - if (session.ended || !session.baseLayer) { + updateBasePoseMatrix() { + if (this.nativeGamepad && this.nativeGamepad.pose) { + let pose = this.nativeGamepad.pose; + let position = pose.position; + let orientation = pose.orientation; + if (!position && !orientation) { return; } - if (session.outputContext && !(session.immersive && !this.display.capabilities.hasExternalDisplay)) { - var mirroring = session.immersive && this.display.capabilities.hasExternalDisplay; - var canvas = session.baseLayer.context.canvas; - var iWidth = mirroring ? canvas.width / 2 : canvas.width; - var iHeight = canvas.height; - { - var outputCanvas = session.outputContext.canvas; - var outputContext = outputCanvas.getContext('2d'); - var oWidth = outputCanvas.width; - var oHeight = outputCanvas.height; - outputContext.drawImage(canvas, 0, 0, iWidth, iHeight, 0, 0, oWidth, oHeight); - } - } - if (session.immersive && session.baseLayer) { - this.display.submitFrame(); - } - } - }, { - key: 'cancelAnimationFrame', - value: function cancelAnimationFrame(handle) { - this.display.cancelAnimationFrame(handle); - } - }, { - key: 'endSession', - value: function endSession(sessionId) { - return new Promise(function ($return, $error) { - var session = this.sessions.get(sessionId); - if (session.ended) { - return $return(); - } - if (session.immersive) { - return $return(this.display.exitPresent()); + if (!position) { + if (!pose.hasPosition) { + if (!this.armModel) { + this.armModel = new OrientationArmModel(); + } + this.armModel.setHandedness(this.nativeGamepad.hand); + this.armModel.update(orientation, this.polyfill.getBasePoseMatrix()); + position = this.armModel.getPosition(); } else { - session.ended = true; + position = this.lastPosition; } - return $return(); - }.bind(this)); - } - }, { - key: 'requestStageBounds', - value: function requestStageBounds() { - if (this.display.stageParameters) { - var width = this.display.stageParameters.sizeX; - var depth = this.display.stageParameters.sizeZ; - var data = []; - data.push(-width / 2); - data.push(-depth / 2); - data.push(width / 2); - data.push(-depth / 2); - data.push(width / 2); - data.push(depth / 2); - data.push(-width / 2); - data.push(depth / 2); - return data; + } else { + this.lastPosition[0] = position[0]; + this.lastPosition[1] = position[1]; + this.lastPosition[2] = position[2]; } - return null; + fromRotationTranslation(this.basePoseMatrix, orientation, position); + } else { + copy(this.basePoseMatrix, this.polyfill.getBasePoseMatrix()); } - }, { - key: 'requestFrameOfReferenceTransform', - value: function requestFrameOfReferenceTransform(type, options) { - return new Promise(function ($return, $error) { - if (type === 'stage' && this.display.stageParameters && this.display.stageParameters.sittingToStandingTransform) { - return $return(this.display.stageParameters.sittingToStandingTransform); + return this.basePoseMatrix; + } + getXRPose(coordinateSystem, poseType) { + this.updateBasePoseMatrix(); + switch(poseType) { + case "target-ray": + coordinateSystem.transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$16].targetRayTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$16].targetRayTransform); } - return $return(); - }.bind(this)); + break; + case "grip": + if (!this.nativeGamepad || !this.nativeGamepad.pose) { + return null; + } + coordinateSystem.transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$16].gripTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$16].gripTransform); + } + break; + default: + return null; } - }, { - key: 'getProjectionMatrix', - value: function getProjectionMatrix(eye) { - if (eye === 'left') { - return this.frame.leftProjectionMatrix; - } else if (eye === 'right') { - return this.frame.rightProjectionMatrix; - } else { - throw new Error('eye must be of type \'left\' or \'right\''); + coordinateSystem._adjustForOriginOffset(this.outputMatrix); + return new XRPose(new XRRigidTransform(this.outputMatrix), this.emulatedPosition); + } +} + +const TEST_ENV = "production" === 'test'; +const EXTRA_PRESENTATION_ATTRIBUTES = { + highRefreshRate: true, +}; +const PRIMARY_BUTTON_MAP = { + oculus: 1, + openvr: 1, + 'spatial controller (spatial interaction source)': 1 +}; +let SESSION_ID = 0; +class Session { + constructor(mode, polyfillOptions={}) { + this.mode = mode; + this.outputContext = null; + this.immersive = mode == 'immersive-vr' || mode == 'immersive-ar'; + this.ended = null; + this.baseLayer = null; + this.inlineVerticalFieldOfView = Math.PI * 0.5; + this.id = ++SESSION_ID; + this.modifiedCanvasLayer = false; + if (this.outputContext && !TEST_ENV) { + const renderContextType = polyfillOptions.renderContextType || '2d'; + this.renderContext = this.outputContext.canvas.getContext(renderContextType); + } + } +} +class WebVRDevice extends XRDevice { + constructor(global, display) { + const { canPresent } = display.capabilities; + super(global); + this.display = display; + this.frame = new global.VRFrameData(); + this.sessions = new Map(); + this.immersiveSession = null; + this.canPresent = canPresent; + this.baseModelMatrix = create(); + this.gamepadInputSources = {}; + this.tempVec3 = new Float32Array(3); + this.onVRDisplayPresentChange = this.onVRDisplayPresentChange.bind(this); + global.window.addEventListener('vrdisplaypresentchange', this.onVRDisplayPresentChange); + this.CAN_USE_GAMEPAD = global.navigator && ('getGamepads' in global.navigator); + this.HAS_BITMAP_SUPPORT = isImageBitmapSupported(global); + } + get depthNear() { return this.display.depthNear; } + set depthNear(val) { this.display.depthNear = val; } + get depthFar() { return this.display.depthFar; } + set depthFar(val) { this.display.depthFar = val; } + onBaseLayerSet(sessionId, layer) { + const session = this.sessions.get(sessionId); + const canvas = layer.context.canvas; + if (session.immersive) { + const left = this.display.getEyeParameters('left'); + const right = this.display.getEyeParameters('right'); + canvas.width = Math.max(left.renderWidth, right.renderWidth) * 2; + canvas.height = Math.max(left.renderHeight, right.renderHeight); + this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES + }]).then(() => { + if (!TEST_ENV && !this.global.document.body.contains(canvas)) { + session.modifiedCanvasLayer = true; + this.global.document.body.appendChild(canvas); + applyCanvasStylesForMinimalRendering(canvas); + } + session.baseLayer = layer; + }); + } + else { + session.baseLayer = layer; + } + } + onInlineVerticalFieldOfViewSet(sessionId, value) { + const session = this.sessions.get(sessionId); + session.inlineVerticalFieldOfView = value; + } + supportsSession(mode) { + if (XRSessionModes.indexOf(mode) == -1) { + throw new TypeError( + `The provided value '${mode}' is not a valid enum value of type XRSessionMode`); + } + if (mode == 'immersive-ar') { + return false; + } + if (mode == 'immersive-vr' && this.canPresent === false) { + return false; + } + return true; + } + async requestSession(mode) { + if (!this.supportsSession(mode)) { + return Promise.reject(); + } + let immersive = mode == 'immersive-vr'; + if (immersive) { + const canvas = this.global.document.createElement('canvas'); + if (!TEST_ENV) { + const ctx = canvas.getContext('webgl'); + } + await this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES }]); + } + const session = new Session(mode, { + renderContextType: this.HAS_BITMAP_SUPPORT ? 'bitmaprenderer' : '2d' + }); + this.sessions.set(session.id, session); + if (immersive) { + this.immersiveSession = session; + this.dispatchEvent('@@webxr-polyfill/vr-present-start', session.id); + } + return Promise.resolve(session.id); + } + requestAnimationFrame(callback) { + return this.display.requestAnimationFrame(callback); + } + getPrimaryButtonIndex(gamepad) { + let primaryButton = 0; + let name = gamepad.id.toLowerCase(); + for (let key in PRIMARY_BUTTON_MAP) { + if (name.includes(key)) { + primaryButton = PRIMARY_BUTTON_MAP[key]; + break; } } - }, { - key: 'getViewport', - value: function getViewport(sessionId, eye, layer, target) { - var session = this.sessions.get(sessionId); - var _layer$context$canvas = layer.context.canvas, - width = _layer$context$canvas.width, - height = _layer$context$canvas.height; - if (!session.immersive) { - target.x = target.y = 0; - target.width = width; - target.height = height; - return true; + return Math.min(primaryButton, gamepad.buttons.length - 1); + } + onFrameStart(sessionId) { + this.display.getFrameData(this.frame); + const session = this.sessions.get(sessionId); + if (session.immersive && this.CAN_USE_GAMEPAD) { + let prevInputSources = this.gamepadInputSources; + this.gamepadInputSources = {}; + let gamepads = this.global.navigator.getGamepads(); + for (let i = 0; i < gamepads.length; ++i) { + let gamepad = gamepads[i]; + if (gamepad && gamepad.displayId > 0) { + let inputSourceImpl = prevInputSources[i]; + if (!inputSourceImpl) { + inputSourceImpl = new GamepadXRInputSource(this, this.getPrimaryButtonIndex(gamepad)); + } + inputSourceImpl.updateFromGamepad(gamepad); + this.gamepadInputSources[i] = inputSourceImpl; + if (inputSourceImpl.primaryButtonIndex != -1) { + let primaryActionPressed = gamepad.buttons[inputSourceImpl.primaryButtonIndex].pressed; + if (primaryActionPressed && !inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-start', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } else if (!primaryActionPressed && inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-end', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } + inputSourceImpl.primaryActionPressed = primaryActionPressed; + } + } } - if (eye === 'left') { - target.x = 0; - } else if (eye === 'right') { - target.x = width / 2; - } else { - return false; + } + if (TEST_ENV) { + return; + } + if (!session.immersive && session.baseLayer) { + const canvas = session.baseLayer.context.canvas; + perspective(this.frame.leftProjectionMatrix, session.inlineVerticalFieldOfView, + canvas.width/canvas.height, this.depthNear, this.depthFar); + } + } + onFrameEnd(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended || !session.baseLayer) { + return; + } + if (session.outputContext && + !(session.immersive && !this.display.capabilities.hasExternalDisplay)) { + const mirroring = + session.immersive && this.display.capabilities.hasExternalDisplay; + const iCanvas = session.baseLayer.context.canvas; + const iWidth = mirroring ? iCanvas.width / 2 : iCanvas.width; + const iHeight = iCanvas.height; + if (!TEST_ENV) { + const oCanvas = session.outputContext.canvas; + const oWidth = oCanvas.width; + const oHeight = oCanvas.height; + const renderContext = session.renderContext; + if (this.HAS_BITMAP_SUPPORT) { + if (iCanvas.transferToImageBitmap) { + renderContext.transferFromImageBitmap(iCanvas.transferToImageBitmap()); + } + else { + this.global.createImageBitmap(iCanvas, 0, 0, iWidth, iHeight, { + resizeWidth: oWidth, + resizeHeight: oHeight, + }).then(bitmap => renderContext.transferFromImageBitmap(bitmap)); + } + } else { + renderContext.drawImage(iCanvas, 0, 0, iWidth, iHeight, + 0, 0, oWidth, oHeight); + } } - target.y = 0; - target.width = width / 2; + } + if (session.immersive && session.baseLayer) { + this.display.submitFrame(); + } + } + cancelAnimationFrame(handle) { + this.display.cancelAnimationFrame(handle); + } + async endSession(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended) { + return; + } + if (session.immersive) { + return this.display.exitPresent(); + } else { + session.ended = true; + } + } + requestStageBounds() { + if (this.display.stageParameters) { + const width = this.display.stageParameters.sizeX; + const depth = this.display.stageParameters.sizeZ; + const data = []; + data.push(-width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(depth / 2); + data.push(-width / 2); + data.push(depth / 2); + return data; + } + return null; + } + async requestFrameOfReferenceTransform(type, options) { + if (type === 'stage' && this.display.stageParameters && + this.display.stageParameters.sittingToStandingTransform) { + return this.display.stageParameters.sittingToStandingTransform; + } + } + getProjectionMatrix(eye) { + if (eye === 'left') { + return this.frame.leftProjectionMatrix; + } else if (eye === 'right') { + return this.frame.rightProjectionMatrix; + } else if (eye === 'none') { + return this.frame.leftProjectionMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); + } + } + getViewport(sessionId, eye, layer, target) { + const session = this.sessions.get(sessionId); + const { width, height } = layer.context.canvas; + if (!session.immersive) { + target.x = target.y = 0; + target.width = width; target.height = height; return true; } - }, { - key: 'getBasePoseMatrix', - value: function getBasePoseMatrix() { - var _frame$pose = this.frame.pose, - position = _frame$pose.position, - orientation = _frame$pose.orientation; - if (!position && !orientation) { - return this.baseModelMatrix; - } - if (!position) { - position = this.tempVec3; - position[0] = position[1] = position[2] = 0; - } - fromRotationTranslation(this.baseModelMatrix, orientation, position); + if (eye === 'left') { + target.x = 0; + } else if (eye === 'right') { + target.x = width / 2; + } else { + return false; + } + target.y = 0; + target.width = width / 2; + target.height = height; + return true; + } + getBasePoseMatrix() { + let { position, orientation } = this.frame.pose; + if (!position && !orientation) { return this.baseModelMatrix; } - }, { - key: 'getBaseViewMatrix', - value: function getBaseViewMatrix(eye) { - if (eye === 'left') { - return this.frame.leftViewMatrix; - } else if (eye === 'right') { - return this.frame.rightViewMatrix; - } else { - throw new Error('eye must be of type \'left\' or \'right\''); - } + if (!position) { + position = this.tempVec3; + position[0] = position[1] = position[2] = 0; } - }, { - key: 'getInputSources', - value: function getInputSources() { - var inputSources = []; - for (var i in this.gamepadInputSources) { - inputSources.push(this.gamepadInputSources[i].inputSource); - } - return inputSources; + fromRotationTranslation(this.baseModelMatrix, orientation, position); + return this.baseModelMatrix; + } + getBaseViewMatrix(eye) { + if (eye === 'left') { + return this.frame.leftViewMatrix; + } else if (eye === 'right') { + return this.frame.rightViewMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); } - }, { - key: 'getInputPose', - value: function getInputPose(inputSource, coordinateSystem) { - if (!coordinateSystem) { - return null; - } - for (var i in this.gamepadInputSources) { - var inputSourceImpl = this.gamepadInputSources[i]; - if (inputSourceImpl.inputSource === inputSource) { - return inputSourceImpl.getXRInputPose(coordinateSystem); - } - } + } + getInputSources() { + let inputSources = []; + for (let i in this.gamepadInputSources) { + inputSources.push(this.gamepadInputSources[i].inputSource); + } + return inputSources; + } + getInputPose(inputSource, coordinateSystem, poseType) { + if (!coordinateSystem) { return null; } - }, { - key: 'onWindowResize', - value: function onWindowResize() {} - }, { - key: 'onVRDisplayPresentChange', - value: function onVRDisplayPresentChange(e) { - var _this3 = this; - if (!this.display.isPresenting) { - this.sessions.forEach(function (session) { - if (session.immersive && !session.ended) { - if (session.modifiedCanvasLayer) { - var canvas = session.baseLayer.context.canvas; - document.body.removeChild(canvas); - canvas.setAttribute('style', ''); - } - if (_this3.immersiveSession === session) { - _this3.immersiveSession = null; - } - _this3.dispatchEvent('@@webxr-polyfill/vr-present-end', session.id); - } - }); + for (let i in this.gamepadInputSources) { + let inputSourceImpl = this.gamepadInputSources[i]; + if (inputSourceImpl.inputSource === inputSource) { + return inputSourceImpl.getXRPose(coordinateSystem, poseType); } } - }, { - key: 'depthNear', - get: function get$$1() { - return this.display.depthNear; + return null; + } + onWindowResize() { + } + onVRDisplayPresentChange(e) { + if (!this.display.isPresenting) { + this.sessions.forEach(session => { + if (session.immersive && !session.ended) { + if (session.modifiedCanvasLayer) { + const canvas = session.baseLayer.context.canvas; + document.body.removeChild(canvas); + canvas.setAttribute('style', ''); + } + if (this.immersiveSession === session) { + this.immersiveSession = null; + } + this.dispatchEvent('@@webxr-polyfill/vr-present-end', session.id); + } + }); } - , - set: function set$$1(val) { - this.display.depthNear = val; - } - }, { - key: 'depthFar', - get: function get$$1() { - return this.display.depthFar; - } - , - set: function set$$1(val) { - this.display.depthFar = val; - } - }]); - return WebVRDevice; -}(PolyfilledXRDevice); + } +} -var CardboardXRDevice = function (_WebVRDevice) { - inherits(CardboardXRDevice, _WebVRDevice); - function CardboardXRDevice(global) { - classCallCheck(this, CardboardXRDevice); - var display = new CardboardVRDisplay(); - var _this = possibleConstructorReturn(this, (CardboardXRDevice.__proto__ || Object.getPrototypeOf(CardboardXRDevice)).call(this, global, display)); - _this.display = display; - _this.frame = { +class CardboardXRDevice extends WebVRDevice { + constructor(global, cardboardConfig) { + const display = new CardboardVRDisplay(cardboardConfig || {}); + super(global, display); + this.display = display; + this.frame = { rightViewMatrix: new Float32Array(16), leftViewMatrix: new Float32Array(16), rightProjectionMatrix: new Float32Array(16), leftProjectionMatrix: new Float32Array(16), pose: null, - timestamp: null + timestamp: null, }; - return _this; } - return CardboardXRDevice; -}(WebVRDevice); +} -var getXRDevice = function getXRDevice(global) { - return new Promise(function ($return, $error) { - var device; - device = null; - if ('xr' in global.navigator) { - var $Try_1_Post = function () { - try { - return $If_3.call(this); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this);var $Try_1_Catch = function (e) { - try { - return $Try_1_Post(); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this); - try { - return Promise.resolve(global.navigator.xr.requestDevice()).then(function ($await_6) { - try { - device = $await_6; - return $Try_1_Post(); - } catch ($boundEx) { - return $Try_1_Catch($boundEx); - } - }.bind(this), $Try_1_Catch); - } catch (e) { - $Try_1_Catch(e); +const getWebVRDevice = async function (global) { + let device = null; + if ('getVRDisplays' in global.navigator) { + try { + const displays = await global.navigator.getVRDisplays(); + if (displays && displays.length) { + device = new WebVRDevice(global, displays[0]); } - } - function $If_3() { - return $return(device); - } - return $If_3.call(this); - }.bind(this)); + } catch (e) {} + } + return device; }; -var getVRDisplay = function getVRDisplay(global) { - return new Promise(function ($return, $error) { - var device, displays; - device = null; - if ('getVRDisplays' in global.navigator) { - var $Try_2_Post = function () { - try { - return $If_4.call(this); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this);var $Try_2_Catch = function (e) { - try { - return $Try_2_Post(); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this); - try { - return Promise.resolve(global.navigator.getVRDisplays()).then(function ($await_7) { - try { - displays = $await_7; - if (displays && displays.length) { - device = new WebVRDevice(global, displays[0]); - } - return $Try_2_Post(); - } catch ($boundEx) { - return $Try_2_Catch($boundEx); - } - }.bind(this), $Try_2_Catch); - } catch (e) { - $Try_2_Catch(e); - } +const requestXRDevice = async function (global, config) { + if (config.webvr) { + let xr = await getWebVRDevice(global); + if (xr) { + return xr; } - function $If_4() { - return $return(device); - } - return $If_4.call(this); - }.bind(this)); -}; -var requestDevice = function requestDevice(global, config) { - return new Promise(function ($return, $error) { - var device; - return Promise.resolve(getXRDevice(global)).then(function ($await_8) { - try { - device = $await_8; - if (device) { - return $return(device); - } - if (config.webvr) { - return Promise.resolve(getVRDisplay(global)).then(function ($await_9) { - try { - device = $await_9; - if (device) { - return $return(new XRDevice(device)); - } - return $If_5.call(this); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this), $error); - } - function $If_5() { - if (config.cardboard && isMobile(global)) { - if (!global.VRFrameData) { - global.VRFrameData = function () { - this.rightViewMatrix = new Float32Array(16); - this.leftViewMatrix = new Float32Array(16); - this.rightProjectionMatrix = new Float32Array(16); - this.leftProjectionMatrix = new Float32Array(16); - this.pose = null; - }; - } - return $return(new XRDevice(new CardboardXRDevice(global))); - } - return $return(null); - } - return $If_5.call(this); - } catch ($boundEx) { - return $error($boundEx); - } - }.bind(this), $error); - }.bind(this)); + } + if (!global.VRFrameData) { + global.VRFrameData = function () { + this.rightViewMatrix = new Float32Array(16); + this.leftViewMatrix = new Float32Array(16); + this.rightProjectionMatrix = new Float32Array(16); + this.leftProjectionMatrix = new Float32Array(16); + this.pose = null; + }; + } + return new CardboardXRDevice(global, config.cardboardConfig); }; -var CONFIG_DEFAULTS = { +const CONFIG_DEFAULTS = { + global: _global, webvr: true, - cardboard: true + cardboard: true, + cardboardConfig: null, + allowCardboardOnDesktop: false, }; -var partials = ['navigator', 'HTMLCanvasElement', 'WebGLRenderingContext']; -var WebXRPolyfill = function () { - function WebXRPolyfill(global) { - var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - classCallCheck(this, WebXRPolyfill); - this.global = global || _global; +const partials = ['navigator', 'HTMLCanvasElement', 'WebGLRenderingContext']; +class WebXRPolyfill { + constructor(config={}) { this.config = Object.freeze(Object.assign({}, CONFIG_DEFAULTS, config)); + this.global = this.config.global; this.nativeWebXR = 'xr' in this.global.navigator; this.injected = false; if (!this.nativeWebXR) { this._injectPolyfill(this.global); } else if (this.config.cardboard && isMobile(this.global)) { - this._patchRequestDevice(); - } + this._patchNavigatorXR(); + } } - createClass(WebXRPolyfill, [{ - key: '_injectPolyfill', - value: function _injectPolyfill(global) { - if (!partials.every(function (iface) { - return !!global[iface]; - })) { - throw new Error('Global must have the following attributes : ' + partials); - } - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; - try { - for (var _iterator = Object.keys(API)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var className = _step.value; - if (global[className] !== undefined) { - console.warn(className + ' already defined on global.'); - } else { - global[className] = API[className]; - } - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } - } - { - var polyfilledCtx = extendContextCompatibleXRDevice(global.WebGLRenderingContext); - if (polyfilledCtx) { - extendGetContext(global.HTMLCanvasElement); - if (global.WebGL2RenderingContext) { - extendContextCompatibleXRDevice(global.WebGL2RenderingContext); - } - } - } - this.injected = true; - this._patchRequestDevice(); + _injectPolyfill(global) { + if (!partials.every(iface => !!global[iface])) { + throw new Error(`Global must have the following attributes : ${partials}`); } - }, { - key: '_patchRequestDevice', - value: function _patchRequestDevice() { - var device = requestDevice(this.global, this.config); - this.xr = new XR(device); - Object.defineProperty(this.global.navigator, 'xr', { - value: this.xr, - configurable: true - }); + for (const className of Object.keys(API)) { + if (global[className] !== undefined) { + console.warn(`${className} already defined on global.`); + } else { + global[className] = API[className]; + } } - }]); - return WebXRPolyfill; -}(); + { + const polyfilledCtx = polyfillMakeXRCompatible(global.WebGLRenderingContext); + if (polyfilledCtx) { + polyfillGetContext(global.HTMLCanvasElement); + if (global.OffscreenCanvas) { + polyfillGetContext(global.OffscreenCanvas); + } + if (global.WebGL2RenderingContext){ + polyfillMakeXRCompatible(global.WebGL2RenderingContext); + } + } + } + this.injected = true; + this._patchNavigatorXR(); + } + _patchNavigatorXR() { + let devicePromise = requestXRDevice(this.global, this.config); + this.xr = new XR(devicePromise); + Object.defineProperty(this.global.navigator, 'xr', { + value: this.xr, + configurable: true, + }); + } +} return WebXRPolyfill;
diff --git a/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.min.js b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.min.js new file mode 100644 index 0000000..bb39d85 --- /dev/null +++ b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.min.js
@@ -0,0 +1,95 @@ +/** + * @license + * webxr-polyfill + * Copyright (c) 2017 Google + * 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. + */ +/** + * @license + * cardboard-vr-display + * Copyright (c) 2015-2017 Google + * 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. + */ +/** + * @license + * webvr-polyfill-dpdb + * Copyright (c) 2017 Google + * 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. + */ +/** + * @license + * wglu-preserve-state + * Copyright (c) 2016, Brandon Jones. + * + * 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. + */ +/** + * @license + * nosleep.js + * Copyright (c) 2017, Rich Tibbett + * + * 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(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.WebXRPolyfill=t()}(this,function(){"use strict";const e="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},t=Symbol("@@webxr-polyfill/EventTarget");class i{constructor(){this[t]={listeners:new Map}}addEventListener(e,i){if("string"!=typeof e)throw new Error("`type` must be a string");if("function"!=typeof i)throw new Error("`listener` must be a function");const r=this[t].listeners.get(e)||[];r.push(i),this[t].listeners.set(e,r)}removeEventListener(e,i){if("string"!=typeof e)throw new Error("`type` must be a string");if("function"!=typeof i)throw new Error("`listener` must be a function");const r=this[t].listeners.get(e)||[];for(let e=r.length;e>=0;e--)r[e]===i&&r.pop()}dispatchEvent(e,i){const r=this[t].listeners.get(e)||[],n=[];for(let e=0;e<r.length;e++)n[e]=r[e];for(let e of n)e(i);"function"==typeof this[`on${e}`]&&this[`on${e}`](i)}}const r=Symbol("@@webxr-polyfill/XR"),n=["inline","immersive-vr","immersive-ar"],s="Polyfill Error: Must call navigator.xr.supportsSession() with any XRSessionMode\nor navigator.xr.requestSession('inline') prior to requesting an immersive\nsession. This is a limitation specific to the WebXR Polyfill and does not apply\nto native implementations of the API.";let a;if("performance"in e==!1){let e=Date.now();a=(()=>Date.now()-e)}else a=(()=>performance.now());var o=a;const l=Symbol("@@webxr-polyfill/XRPose");class A{constructor(e,t){this[l]={transform:e,emulatedPosition:t}}get transform(){return this[l].transform}get emulatedPosition(){return this[l].emulatedPosition}_setTransform(e){this[l].transform=e}}const h=1e-6;let c="undefined"!=typeof Float32Array?Float32Array:Array;Math.PI;function d(){let e=new c(16);return c!=Float32Array&&(e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[11]=0,e[12]=0,e[13]=0,e[14]=0),e[0]=1,e[5]=1,e[10]=1,e[15]=1,e}function u(e,t){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 p(e){return e[0]=1,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=1,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=1,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,e}function f(e,t){let i=t[0],r=t[1],n=t[2],s=t[3],a=t[4],o=t[5],l=t[6],A=t[7],h=t[8],c=t[9],d=t[10],u=t[11],p=t[12],f=t[13],m=t[14],g=t[15],w=i*o-r*a,v=i*l-n*a,y=i*A-s*a,b=r*l-n*o,E=r*A-s*o,M=n*A-s*l,_=h*f-c*p,x=h*m-d*p,F=h*g-u*p,S=c*m-d*f,R=c*g-u*f,T=d*g-u*m,B=w*T-v*R+y*S+b*F-E*x+M*_;return B?(B=1/B,e[0]=(o*T-l*R+A*S)*B,e[1]=(n*R-r*T-s*S)*B,e[2]=(f*M-m*E+g*b)*B,e[3]=(d*E-c*M-u*b)*B,e[4]=(l*F-a*T-A*x)*B,e[5]=(i*T-n*F+s*x)*B,e[6]=(m*y-p*M-g*v)*B,e[7]=(h*M-d*y+u*v)*B,e[8]=(a*R-o*F+A*_)*B,e[9]=(r*F-i*R-s*_)*B,e[10]=(p*E-f*y+g*w)*B,e[11]=(c*y-h*E-u*w)*B,e[12]=(o*x-a*S-l*_)*B,e[13]=(i*S-r*x+n*_)*B,e[14]=(f*v-p*b-m*w)*B,e[15]=(h*b-c*v+d*w)*B,e):null}function m(e,t,i){let r=t[0],n=t[1],s=t[2],a=t[3],o=t[4],l=t[5],A=t[6],h=t[7],c=t[8],d=t[9],u=t[10],p=t[11],f=t[12],m=t[13],g=t[14],w=t[15],v=i[0],y=i[1],b=i[2],E=i[3];return e[0]=v*r+y*o+b*c+E*f,e[1]=v*n+y*l+b*d+E*m,e[2]=v*s+y*A+b*u+E*g,e[3]=v*a+y*h+b*p+E*w,v=i[4],y=i[5],b=i[6],E=i[7],e[4]=v*r+y*o+b*c+E*f,e[5]=v*n+y*l+b*d+E*m,e[6]=v*s+y*A+b*u+E*g,e[7]=v*a+y*h+b*p+E*w,v=i[8],y=i[9],b=i[10],E=i[11],e[8]=v*r+y*o+b*c+E*f,e[9]=v*n+y*l+b*d+E*m,e[10]=v*s+y*A+b*u+E*g,e[11]=v*a+y*h+b*p+E*w,v=i[12],y=i[13],b=i[14],E=i[15],e[12]=v*r+y*o+b*c+E*f,e[13]=v*n+y*l+b*d+E*m,e[14]=v*s+y*A+b*u+E*g,e[15]=v*a+y*h+b*p+E*w,e}function g(e,t,i){let r=t[0],n=t[1],s=t[2],a=t[3],o=r+r,l=n+n,A=s+s,h=r*o,c=r*l,d=r*A,u=n*l,p=n*A,f=s*A,m=a*o,g=a*l,w=a*A;return e[0]=1-(u+f),e[1]=c+w,e[2]=d-g,e[3]=0,e[4]=c-w,e[5]=1-(h+f),e[6]=p+m,e[7]=0,e[8]=d+g,e[9]=p-m,e[10]=1-(h+u),e[11]=0,e[12]=i[0],e[13]=i[1],e[14]=i[2],e[15]=1,e}function w(e,t){return e[0]=t[12],e[1]=t[13],e[2]=t[14],e}function v(e,t){let i=t[0]+t[5]+t[10],r=0;return i>0?(r=2*Math.sqrt(i+1),e[3]=.25*r,e[0]=(t[6]-t[9])/r,e[1]=(t[8]-t[2])/r,e[2]=(t[1]-t[4])/r):t[0]>t[5]&&t[0]>t[10]?(r=2*Math.sqrt(1+t[0]-t[5]-t[10]),e[3]=(t[6]-t[9])/r,e[0]=.25*r,e[1]=(t[1]+t[4])/r,e[2]=(t[8]+t[2])/r):t[5]>t[10]?(r=2*Math.sqrt(1+t[5]-t[0]-t[10]),e[3]=(t[8]-t[2])/r,e[0]=(t[1]+t[4])/r,e[1]=.25*r,e[2]=(t[6]+t[9])/r):(r=2*Math.sqrt(1+t[10]-t[0]-t[5]),e[3]=(t[1]-t[4])/r,e[0]=(t[8]+t[2])/r,e[1]=(t[6]+t[9])/r,e[2]=.25*r),e}function y(){let e=new c(3);return c!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0),e}function b(e){var t=new c(3);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t}function E(e,t,i){let r=new c(3);return r[0]=e,r[1]=t,r[2]=i,r}function M(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e}function _(e,t,i){return e[0]=t[0]+i[0],e[1]=t[1]+i[1],e[2]=t[2]+i[2],e}function x(e,t,i){return e[0]=t[0]*i,e[1]=t[1]*i,e[2]=t[2]*i,e}function F(e,t){let i=t[0],r=t[1],n=t[2],s=i*i+r*r+n*n;return s>0&&(s=1/Math.sqrt(s),e[0]=t[0]*s,e[1]=t[1]*s,e[2]=t[2]*s),e}function S(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]}function R(e,t,i){let r=t[0],n=t[1],s=t[2],a=i[0],o=i[1],l=i[2];return e[0]=n*l-s*o,e[1]=s*a-r*l,e[2]=r*o-n*a,e}function T(e,t,i){let r=i[0],n=i[1],s=i[2],a=i[3],o=t[0],l=t[1],A=t[2],h=n*A-s*l,c=s*o-r*A,d=r*l-n*o,u=n*d-s*c,p=s*h-r*d,f=r*c-n*h,m=2*a;return h*=m,c*=m,d*=m,u*=2,p*=2,f*=2,e[0]=o+h+u,e[1]=l+c+p,e[2]=A+d+f,e}const B=function(e){let t=e[0],i=e[1],r=e[2];return Math.sqrt(t*t+i*i+r*r)};!function(){let e=y()}();!function(){let e=function(){let e=new c(4);return c!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0,e[3]=0),e}()}();function C(){let e=new c(4);return c!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0),e[3]=1,e}function P(e,t,i){let r=t[0],n=t[1],s=t[2],a=t[3],o=i[0],l=i[1],A=i[2],h=i[3];return e[0]=r*h+a*o+n*A-s*l,e[1]=n*h+a*l+s*o-r*A,e[2]=s*h+a*A+r*l-n*o,e[3]=a*h-r*o-n*l-s*A,e}function D(e,t,i,r){let n,s,a,o,l,A=t[0],c=t[1],d=t[2],u=t[3],p=i[0],f=i[1],m=i[2],g=i[3];return(s=A*p+c*f+d*m+u*g)<0&&(s=-s,p=-p,f=-f,m=-m,g=-g),1-s>h?(n=Math.acos(s),a=Math.sin(n),o=Math.sin((1-r)*n)/a,l=Math.sin(r*n)/a):(o=1-r,l=r),e[0]=o*A+l*p,e[1]=o*c+l*f,e[2]=o*d+l*m,e[3]=o*u+l*g,e}function I(e,t){let i=t[0],r=t[1],n=t[2],s=t[3],a=i*i+r*r+n*n+s*s,o=a?1/a:0;return e[0]=-i*o,e[1]=-r*o,e[2]=-n*o,e[3]=s*o,e}const L=function(e){let t=new c(4);return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},O=function(e,t,i,r){let n=new c(4);return n[0]=e,n[1]=t,n[2]=i,n[3]=r,n},Q=function(e,t){return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},N=function(e,t){let i=t[0],r=t[1],n=t[2],s=t[3],a=i*i+r*r+n*n+s*s;return a>0&&(a=1/Math.sqrt(a),e[0]=i*a,e[1]=r*a,e[2]=n*a,e[3]=s*a),e},G=(function(){let e=y(),t=E(1,0,0),i=E(0,1,0)}(),function(){let e=C(),t=C()}(),function(){let e=function(){let e=new c(9);return c!=Float32Array&&(e[1]=0,e[2]=0,e[3]=0,e[5]=0,e[6]=0,e[7]=0),e[0]=1,e[4]=1,e[8]=1,e}()}(),Symbol("@@webxr-polyfill/XRRigidTransform"));class k{constructor(){if(this[G]={matrix:null,position:null,orientation:null,inverse:null},0===arguments.length)this[G].matrix=p(new Float32Array(16));else if(1===arguments.length)arguments[0]instanceof Float32Array?this[G].matrix=arguments[0]:(this[G].position=this._getPoint(arguments[0]),this[G].orientation=DOMPointReadOnly.fromPoint({x:0,y:0,z:0,w:1}));else{if(2!==arguments.length)throw new Error("Too many arguments!");this[G].position=this._getPoint(arguments[0]),this[G].orientation=this._getPoint(arguments[1])}if(this[G].matrix){let e=y();w(e,this[G].matrix),this[G].position=DOMPointReadOnly.fromPoint({x:e[0],y:e[1],z:e[2]});let t=C();v(t,this[G].matrix),this[G].orientation=DOMPointReadOnly.fromPoint({x:t[0],y:t[1],z:t[2],w:t[3]})}else this[G].matrix=p(new Float32Array(16)),g(this[G].matrix,O(this[G].orientation.x,this[G].orientation.y,this[G].orientation.z,this[G].orientation.w),E(this[G].position.x,this[G].position.y,this[G].position.z))}_getPoint(e){return e instanceof DOMPointReadOnly?e:DOMPointReadOnly.fromPoint(e)}get matrix(){return this[G].matrix}get position(){return this[G].position}get orientation(){return this[G].orientation}get inverse(){if(null===this[G].inverse){let e=p(new Float32Array(16));f(e,this[G].matrix),this[G].inverse=new k(e),this[G].inverse[G].inverse=this}return this[G].inverse}}const V=Symbol("@@webxr-polyfill/XRViewerPose");class z extends A{constructor(e){super(new k,!1),this[V]={device:e,leftViewMatrix:p(new Float32Array(16)),rightViewMatrix:p(new Float32Array(16)),poseModelMatrix:p(new Float32Array(16))}}get poseModelMatrix(){return this[V].poseModelMatrix}getViewMatrix(e){switch(e.eye){case"left":return this[V].leftViewMatrix;case"right":return this[V].rightViewMatrix}throw new Error("view is not a valid XREye")}get views(){return this[V].views}set views(e){this[V].views=e}updateFromReferenceSpace(e){const t=this[V].device.getBasePoseMatrix(),i=this[V].device.getBaseViewMatrix("left"),r=this[V].device.getBaseViewMatrix("right");t&&(e.transformBasePoseMatrix(this[V].poseModelMatrix,t),e._adjustForOriginOffset(this[V].poseModelMatrix),super._setTransform(new k(this[V].poseModelMatrix))),i&&r&&(e.transformBaseViewMatrix(this[V].leftViewMatrix,i,this[V].poseModelMatrix),e.transformBaseViewMatrix(this[V].rightViewMatrix,r,this[V].poseModelMatrix),m(this[V].leftViewMatrix,this[V].leftViewMatrix,e._originOffsetMatrix()),m(this[V].rightViewMatrix,this[V].rightViewMatrix,e._originOffsetMatrix()));for(let e of this[V].views)"left"==e.eye?e._updateViewMatrix(this[V].leftViewMatrix):"right"==e.eye&&e._updateViewMatrix(this[V].rightViewMatrix)}}const U=Symbol("@@webxr-polyfill/XRViewport");class H{constructor(e){this[U]={target:e}}get x(){return this[U].target.x}get y(){return this[U].target.y}get width(){return this[U].target.width}get height(){return this[U].target.height}}const X=["left","right"],W=Symbol("@@webxr-polyfill/XRView");class j{constructor(e,t,i){if(!X.includes(t))throw new Error(`XREye must be one of: ${X}`);const r=Object.create(null),n=new H(r);this[W]={device:e,eye:t,viewport:n,temp:r,sessionId:i,transform:null}}get eye(){return this[W].eye}get projectionMatrix(){return this[W].device.getProjectionMatrix(this.eye)}get transform(){return this[W].transform}_updateViewMatrix(e){let t=p(new Float32Array(16));f(t,e),this[W].transform=new k(t)}_getViewport(e){this[W].viewport;if(this[W].device.getViewport(this[W].sessionId,this.eye,e,this[W].temp))return this[W].viewport}}var Y=1e-6,Z="undefined"!=typeof Float32Array?Float32Array:Array;Math.PI;function q(){var e=new Z(3);return Z!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0),e}function J(e,t,i){var r=new Z(3);return r[0]=e,r[1]=t,r[2]=i,r}function K(e,t,i){var r=t[0],n=t[1],s=t[2],a=i[0],o=i[1],l=i[2];return e[0]=n*l-s*o,e[1]=s*a-r*l,e[2]=r*o-n*a,e}var $,ee=function(e){var t=e[0],i=e[1],r=e[2];return Math.sqrt(t*t+i*i+r*r)};$=q();!function(){var e,t=(e=new Z(4),Z!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0,e[3]=0),e)}();function te(){var e=new Z(4);return Z!=Float32Array&&(e[0]=0,e[1]=0,e[2]=0),e[3]=1,e}function ie(e,t,i,r){var n=t[0],s=t[1],a=t[2],o=t[3],l=i[0],A=i[1],h=i[2],c=i[3],d=void 0,u=void 0,p=void 0,f=void 0,m=void 0;return(u=n*l+s*A+a*h+o*c)<0&&(u=-u,l=-l,A=-A,h=-h,c=-c),1-u>Y?(d=Math.acos(u),p=Math.sin(d),f=Math.sin((1-r)*d)/p,m=Math.sin(r*d)/p):(f=1-r,m=r),e[0]=f*n+m*l,e[1]=f*s+m*A,e[2]=f*a+m*h,e[3]=f*o+m*c,e}var re,ne,se,ae,oe,le,Ae,he=function(e,t){var i=t[0],r=t[1],n=t[2],s=t[3],a=i*i+r*r+n*n+s*s;return a>0&&(a=1/Math.sqrt(a),e[0]=i*a,e[1]=r*a,e[2]=n*a,e[3]=s*a),e};re=q(),ne=J(1,0,0),se=J(0,1,0),ae=te(),oe=te(),le=new Z(9),Z!=Float32Array&&(le[1]=0,le[2]=0,le[3]=0,le[5]=0,le[6]=0,le[7]=0),le[0]=1,le[4]=1,le[8]=1,Ae=le;!function(){var e,t=(e=new Z(2),Z!=Float32Array&&(e[0]=0,e[1]=0),e)}();const ce=Symbol("@@webxr-polyfill/XRFrame");class de{constructor(e,t,i){const r=new z(e),n=[new j(e,"left",i)];t.immersive&&n.push(new j(e,"right",i)),r.views=n,this[ce]={device:e,viewerPose:r,views:n,session:t}}get session(){return this[ce].session}get views(){return this[ce].views}getViewerPose(e){return this[ce].viewerPose.updateFromReferenceSpace(e),this[ce].viewerPose}getPose(e,t){if("viewer"===e._specialType){let e=this.getViewerPose(t);return new XRPose(new XRRigidTransform(e.poseModelMatrix),e.emulatedPosition)}return"target-ray"===e._specialType||"grip"===e._specialType?this[ce].device.getInputPose(e._inputSource,t,e._specialType):null}}const ue=Symbol("@@webxr-polyfill/XRStageBoundsPoint");class pe{constructor(e,t){this[ue]={x:e,z:t}}get x(){return this[ue].x}get z(){return this[ue].z}}const fe=Symbol("@@webxr-polyfill/XRStageBounds");class me{constructor(e){const t=[];for(let i=0;i<e.length;i+=2)t.push(new pe(e[i],e[i+1]));this[fe]={geometry:t}}get geometry(){return this[fe].geometry}}const ge=Symbol("@@webxr-polyfill/XRSpace");class we{constructor(e=null,t=null){this[ge]={specialType:e,inputSource:t}}get _specialType(){return this[ge].specialType}get _inputSource(){return this[ge].inputSource}}const ve=1.6,ye=Symbol("@@webxr-polyfill/XRReferenceSpace"),be=["viewer","local","local-floor","bounded-floor","unbounded"],Ee=Object.freeze({disableStageEmulation:!1,stageEmulationHeight:0});class Me extends we{constructor(e,t,i,r,n){if(i=Object.assign({},Ee,i),!be.includes(t))throw new Error(`XRReferenceSpaceType must be one of ${be}`);if(super("viewer"===t?"viewer":null),this._isFloor(t)&&i.disableStageEmulation&&!r)throw new Error("XRReferenceSpace cannot use 'bounded-floor' type, if disabling emulation and platform does not provide");const{disableStageEmulation:s,stageEmulationHeight:a}=i;let o=0;this._isFloor(t)&&!r&&(o=0!==a?a:ve),this._isFloor(t)&&!r&&((r=p(new Float32Array(16)))[13]=o),r||(r=p(new Float32Array(16))),this[ye]={disableStageEmulation:s,stageEmulationHeight:a,emulatedHeight:o,type:t,transform:r,device:e,bounds:n,options:i,originOffset:p(new Float32Array(16))},this.onboundschange=void 0}_isFloor(e){return"bounded-floor"===e||"local-floor"===e}get bounds(){return this[ye].bounds}get emulatedHeight(){return this[ye].emulatedHeight}get type(){return this[ye].type}transformBasePoseMatrix(e,t){if(this[ye].transform)m(e,this[ye].transform,t);else switch(this.type){case"local":return void(e!==t&&u(e,t))}}transformBaseViewMatrix(e,t){let i=this[ye].transform;return i?(f(e,i),m(e,t,e)):u(e,t),e}_inverseOriginOffsetMatrix(){let e=p(new Float32Array(16));return f(e,this[ye].originOffset),e}_originOffsetMatrix(){return this[ye].originOffset}_adjustForOriginOffset(e){m(e,this._inverseOriginOffsetMatrix(),e)}getOffsetReferenceSpace(e){let t=new Me(this[ye].device,this[ye].type,this[ye].options,this[ye].transform,this[ye].bounds);return m(t[ye].originOffset,this[ye].originOffset,e.matrix),t}}const _e=Symbol("@@webxr-polyfill/polyfilled-xr-compatible"),xe=Symbol("@@webxr-polyfill/xr-compatible"),Fe=Symbol("@@webxr-polyfill/XRWebGLLayer"),Se=Object.freeze({antialias:!0,depth:!1,stencil:!1,alpha:!0,multiview:!1,ignoreDepthValues:!1,framebufferScaleFactor:1});const Re=Symbol("@@webxr-polyfill/XRSession");class Te extends i{constructor(e,t,i){super();let r="inline"!=t;this[Re]={device:e,mode:t,immersive:r,outputContext:null,ended:!1,suspended:!1,suspendedCallback:null,id:i,activeRenderState:null,pendingRenderState:null};const n=new de(e,this,this[Re].id);this[Re].frame=n,this[Re].onPresentationEnd=(t=>{if(t!==this[Re].id){this[Re].suspended=!1,this.dispatchEvent("focus",{session:this});const e=this[Re].suspendedCallback;return this[Re].suspendedCallback=null,void(e&&this.requestAnimationFrame(e))}this[Re].ended=!0,e.removeEventListener("@webvr-polyfill/vr-present-end",this[Re].onPresentationEnd),e.removeEventListener("@webvr-polyfill/vr-present-start",this[Re].onPresentationStart),e.removeEventListener("@@webvr-polyfill/input-select-start",this[Re].onSelectStart),e.removeEventListener("@@webvr-polyfill/input-select-end",this[Re].onSelectEnd),this.dispatchEvent("end",{session:this})}),e.addEventListener("@@webxr-polyfill/vr-present-end",this[Re].onPresentationEnd),this[Re].onPresentationStart=(e=>{e!==this[Re].id&&(this[Re].suspended=!0,this.dispatchEvent("blur",{session:this}))}),e.addEventListener("@@webxr-polyfill/vr-present-start",this[Re].onPresentationStart),this[Re].onSelectStart=(e=>{e.sessionId===this[Re].id&&this.dispatchEvent("selectstart",{frame:this[Re].frame,inputSource:e.inputSource})}),e.addEventListener("@@webxr-polyfill/input-select-start",this[Re].onSelectStart),this[Re].onSelectEnd=(e=>{e.sessionId===this[Re].id&&(this.dispatchEvent("selectend",{frame:this[Re].frame,inputSource:e.inputSource}),this.dispatchEvent("select",{frame:this[Re].frame,inputSource:e.inputSource}))}),e.addEventListener("@@webxr-polyfill/input-select-end",this[Re].onSelectEnd),this.onblur=void 0,this.onfocus=void 0,this.onresetpose=void 0,this.onend=void 0,this.onselect=void 0,this.onselectstart=void 0,this.onselectend=void 0}get renderState(){return this[Re].activeRenderState}get immersive(){return this[Re].immersive}get outputContext(){return this[Re].outputContext}get depthNear(){return this[Re].device.depthNear}set depthNear(e){this[Re].device.depthNear=e}get depthFar(){return this[Re].device.depthFar}set depthFar(e){this[Re].device.depthFar=e}get environmentBlendMode(){return this[Re].device.environmentBlendMode||"opaque"}get baseLayer(){return this[Re].baseLayer}set baseLayer(e){this[Re].ended||(this[Re].baseLayer=e,this[Re].device.onBaseLayerSet(this[Re].id,e))}async requestReferenceSpace(e,t={}){if(this[Re].ended)return;if(t=Object.assign({},Ee,t),!be.includes(e))throw new TypeError(`XRReferenceSpaceType must be one of ${be}`);let i=null,r=null;try{i=await this[Re].device.requestFrameOfReferenceTransform(e,t)}catch(i){if("stage"!==e||t.disableStageEmulation)throw i}return"stage"===e&&i&&(r=this[Re].device.requestStageBounds())&&(r=new me(r)),new Me(this[Re].device,e,t,i,r)}requestAnimationFrame(e){if(!(this[Re].ended||this[Re].suspended&&this[Re].suspendedCallback))return this[Re].suspended&&!this[Re].suspendedCallback&&(this[Re].suspendedCallback=e),this[Re].device.requestAnimationFrame(()=>{null!==this[Re].pendingRenderState&&(this[Re].activeRenderState=this[Re].pendingRenderState,this[Re].pendingRenderState=null,this[Re].activeRenderState.baseLayer&&this[Re].device.onBaseLayerSet(this[Re].id,this[Re].activeRenderState.baseLayer),this[Re].activeRenderState.inlineVerticalFieldOfView&&this[Re].device.onInlineVerticalFieldOfViewSet(this[Re].id,this[Re].activeRenderState.inlineVerticalFieldOfView)),this[Re].device.onFrameStart(this[Re].id),e(o(),this[Re].frame),this[Re].device.onFrameEnd(this[Re].id)})}cancelAnimationFrame(e){this[Re].ended||this[Re].device.cancelAnimationFrame(e)}get inputSources(){return this[Re].device.getInputSources()}async end(){if(!this[Re].ended)return this.immersive||(this[Re].ended=!0,this[Re].device.removeEventListener("@@webvr-polyfill/vr-present-start",this[Re].onPresentationStart),this[Re].device.removeEventListener("@@webvr-polyfill/vr-present-end",this[Re].onPresentationEnd),this[Re].device.removeEventListener("@@webvr-polyfill/input-select-start",this[Re].onSelectStart),this[Re].device.removeEventListener("@@webvr-polyfill/input-select-end",this[Re].onSelectEnd),this.dispatchEvent("end",{session:this})),this[Re].device.endSession(this[Re].id)}updateRenderState(e){if(this[Re].ended){throw new Error("Can't call updateRenderState on an XRSession that has already ended.")}if(e.baseLayer&&e.baseLayer._session!==this){throw new Error("Called updateRenderState with a base layer that was created by a different session.")}if(null!==e.inlineVerticalFieldOfView&&void 0!==e.inlineVerticalFieldOfView){if(this[Re].immersive){throw new Error("inlineVerticalFieldOfView must not be set for an XRRenderState passed to updateRenderState for an immersive session.")}e.inlineVerticalFieldOfView=Math.min(3.13,Math.max(.01,e.inlineVerticalFieldOfView))}null===this[Re].pendingRenderState&&(this[Re].pendingRenderState=Object.assign({},this[Re].activeRenderState,e))}}const Be=Symbol("@@webxr-polyfill/XRInputSource");class Ce{constructor(e){this[Be]={impl:e,gripSpace:new we("grip",this),targetRaySpace:new we("target-ray",this)}}get handedness(){return this[Be].impl.handedness}get targetRayMode(){return this[Be].impl.targetRayMode}get gripSpace(){let e=this[Be].impl.targetRayMode;return"gaze"===e||"screen"===e?null:this[Be].gripSpace}get targetRaySpace(){return this[Be].targetRaySpace}get gamepad(){return this[Be].impl.gamepad}}const Pe=Symbol("@@webxr-polyfill/XRRenderState"),De=Object.freeze({depthNear:.1,depthFar:1e3,inlineVerticalFieldOfView:null,baseLayer:null});var Ie={XR:class extends i{constructor(e){super(),this[r]={device:null,devicePromise:e,immersiveSession:null,inlineSessions:new Set},e.then(e=>{this[r].device=e})}async supportsSession(e){return this[r].device||await this[r].devicePromise,"inline"==e||this[r].device.supportsSession(e)?Promise.resolve(null):Promise.reject(null)}async requestSession(e){if(!this[r].device){if("inline"!=e)throw new Error(s);await this[r].devicePromise}const t=await this[r].device.requestSession(e),i=new XRSession(this[r].device,e,t);"inline"==e?this[r].inlineSessions.add(i):this[r].immersiveSession=i;const n=()=>{"inline"==e?this[r].inlineSessions.delete(i):this[r].immersiveSession=null,i.removeEventListener("end",n)};return i.addEventListener("end",n),i}},XRSession:Te,XRFrame:de,XRView:j,XRViewport:H,XRViewerPose:z,XRWebGLLayer:class{constructor(e,t,i={}){const r=Object.assign({},Se,i);if(!(e instanceof Te))throw new Error("session must be a XRSession");if(e.ended)throw new Error("InvalidStateError");if(t[_e]&&!0!==t[xe])throw new Error("InvalidStateError");const n=t.getParameter(t.FRAMEBUFFER_BINDING);this[Fe]={context:t,config:r,framebuffer:n,session:e}}get context(){return this[Fe].context}get antialias(){return this[Fe].config.antialias}get ignoreDepthValues(){return!0}get framebuffer(){return this[Fe].framebuffer}get framebufferWidth(){return this[Fe].context.drawingBufferWidth}get framebufferHeight(){return this[Fe].context.drawingBufferHeight}get _session(){return this[Fe].session}getViewport(e){return e._getViewport(this)}},XRSpace:we,XRReferenceSpace:Me,XRStageBounds:me,XRStageBoundsPoint:pe,XRInputSource:Ce,XRRenderState:class{constructor(e={}){const t=Object.assign({},De,e);this[Pe]={config:t}}get depthNear(){return this[Pe].depthNear}get depthFar(){return this[Pe].depthFar}get inlineVerticalFieldOfView(){return this[Pe].inlineVerticalFieldOfView}get baseLayer(){return this[Pe].baseLayer}},XRRigidTransform:k,XRPose:A};const Le=e=>"function"!=typeof e.prototype.makeXRCompatible&&(e.prototype.makeXRCompatible=function(){return this[xe]=!0,Promise.resolve()},!0),Oe=e=>{const t=e.prototype.getContext;e.prototype.getContext=function(e,i){const r=t.call(this,e,i);return r[_e]=!0,i&&"xrCompatible"in i&&(r[xe]=i.xrCompatible),r}},Qe=e=>!(!e.ImageBitmapRenderingContext||!e.createImageBitmap),Ne=e=>{var t=!1;return Ge=e.navigator.userAgent||e.navigator.vendor||e.opera,(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(Ge)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(Ge.substr(0,4)))&&(t=!0),t};var Ge;const ke=e=>{e.style.display="block",e.style.position="absolute",e.style.width=e.style.height="1px",e.style.top=e.style.left="0px"};var Ve="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var ze,Ue,He=(function(e,t){e.exports=function(){var e,t,i,r=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var i=0;i<t.length;i++){var r=t[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,i,r){return i&&e(t.prototype,i),r&&e(t,r),t}}(),s=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var i=[],r=!0,n=!1,s=void 0;try{for(var a,o=e[Symbol.iterator]();!(r=(a=o.next()).done)&&(i.push(a.value),!t||i.length!==t);r=!0);}catch(e){n=!0,s=e}finally{try{!r&&o.return&&o.return()}finally{if(n)throw s}}return i}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")},a=function(e,t,i){return e+(t-e)*i},o=function(){var e=/iPad|iPhone|iPod/.test(navigator.platform);return function(){return e}}(),l=function(){var e=-1!==navigator.userAgent.indexOf("Version")&&-1!==navigator.userAgent.indexOf("Android")&&-1!==navigator.userAgent.indexOf("Chrome");return function(){return e}}(),A=(/^((?!chrome|android).)*safari/i.test(navigator.userAgent),function(){var e=-1!==navigator.userAgent.indexOf("Firefox")&&-1!==navigator.userAgent.indexOf("Android");return function(){return e}}()),h=(t=navigator.userAgent.match(/.*Chrome\/([0-9]+)/),i=t?parseInt(t[1],10):null,function(){return i}),c=function(){var e=!1;if(65===h()){var t=navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/);if(t){var i=t[1].split("."),r=s(i,4),n=(r[0],r[1],r[2]),a=r[3];e=3325===parseInt(n,10)&&parseInt(a,10)<148}}return function(){return e}}(),d=function(){var e=-1!==navigator.userAgent.indexOf("R7 Build");return function(){return e}}(),u=function(){var e=90==window.orientation||-90==window.orientation;return d()?!e:e},p=function(){return Math.max(window.screen.width,window.screen.height)*window.devicePixelRatio},f=function(){return Math.min(window.screen.width,window.screen.height)*window.devicePixelRatio},m=function(){if(document.exitFullscreen)document.exitFullscreen();else if(document.webkitExitFullscreen)document.webkitExitFullscreen();else if(document.mozCancelFullScreen)document.mozCancelFullScreen();else{if(!document.msExitFullscreen)return!1;document.msExitFullscreen()}return!0},g=function(e,t,i,r){var n=e.createShader(e.VERTEX_SHADER);e.shaderSource(n,t),e.compileShader(n);var s=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(s,i),e.compileShader(s);var a=e.createProgram();for(var o in e.attachShader(a,n),e.attachShader(a,s),r)e.bindAttribLocation(a,r[o],o);return e.linkProgram(a),e.deleteShader(n),e.deleteShader(s),a},w=function(e,t){for(var i={},r=e.getProgramParameter(t,e.ACTIVE_UNIFORMS),n="",s=0;s<r;s++){var a=e.getActiveUniform(t,s);n=a.name.replace("[0]",""),i[n]=e.getUniformLocation(t,n)}return i},v=function(){var e,t=!1;return e=navigator.userAgent||navigator.vendor||window.opera,(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(e)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0,4)))&&(t=!0),t},y=function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e},b=function(e){if(o()){var t=e.style.width,i=e.style.height;e.style.width=parseInt(t)+1+"px",e.style.height=parseInt(i)+"px",setTimeout(function(){e.style.width=t,e.style.height=i},100)}window.canvas=e},E=function(){var e=Math.PI/180,t=.25*Math.PI,i=new Float32Array([0,0,0,1]),r=new Float32Array([0,0,0]);function n(n,s,a,o,l,A){!function(i,r,n,s){var a=Math.tan(r?r.upDegrees*e:t),o=Math.tan(r?r.downDegrees*e:t),l=Math.tan(r?r.leftDegrees*e:t),A=Math.tan(r?r.rightDegrees*e:t),h=2/(l+A),c=2/(a+o);i[0]=h,i[1]=0,i[2]=0,i[3]=0,i[4]=0,i[5]=c,i[6]=0,i[7]=0,i[8]=-(l-A)*h*.5,i[9]=(a-o)*c*.5,i[10]=s/(n-s),i[11]=-1,i[12]=0,i[13]=0,i[14]=s*n/(n-s),i[15]=0}(n,o||null,A.depthNear,A.depthFar);var h,c,d,u,p,f,m,g,w,v,y,b,E,M,_,x,F,S,R,T=a.orientation||i,B=a.position||r;h=s,d=B,u=(c=T)[0],p=c[1],f=c[2],m=c[3],y=u*(g=u+u),b=u*(w=p+p),E=u*(v=f+f),M=p*w,_=p*v,x=f*v,F=m*g,S=m*w,R=m*v,h[0]=1-(M+x),h[1]=b+R,h[2]=E-S,h[3]=0,h[4]=b-R,h[5]=1-(y+x),h[6]=_+F,h[7]=0,h[8]=E+S,h[9]=_-F,h[10]=1-(y+M),h[11]=0,h[12]=d[0],h[13]=d[1],h[14]=d[2],h[15]=1,l&&function(e,t,i){var r,n,s,a,o,l,A,h,c,d,u,p,f=i[0],m=i[1],g=i[2];t===e?(e[12]=t[0]*f+t[4]*m+t[8]*g+t[12],e[13]=t[1]*f+t[5]*m+t[9]*g+t[13],e[14]=t[2]*f+t[6]*m+t[10]*g+t[14],e[15]=t[3]*f+t[7]*m+t[11]*g+t[15]):(r=t[0],n=t[1],s=t[2],a=t[3],o=t[4],l=t[5],A=t[6],h=t[7],c=t[8],d=t[9],u=t[10],p=t[11],e[0]=r,e[1]=n,e[2]=s,e[3]=a,e[4]=o,e[5]=l,e[6]=A,e[7]=h,e[8]=c,e[9]=d,e[10]=u,e[11]=p,e[12]=r*f+o*m+c*g+t[12],e[13]=n*f+l*m+d*g+t[13],e[14]=s*f+A*m+u*g+t[14],e[15]=a*f+h*m+p*g+t[15])}(s,s,l),function(e,t){var i=t[0],r=t[1],n=t[2],s=t[3],a=t[4],o=t[5],l=t[6],A=t[7],h=t[8],c=t[9],d=t[10],u=t[11],p=t[12],f=t[13],m=t[14],g=t[15],w=i*o-r*a,v=i*l-n*a,y=i*A-s*a,b=r*l-n*o,E=r*A-s*o,M=n*A-s*l,_=h*f-c*p,x=h*m-d*p,F=h*g-u*p,S=c*m-d*f,R=c*g-u*f,T=d*g-u*m,B=w*T-v*R+y*S+b*F-E*x+M*_;if(!B)return null;B=1/B,e[0]=(o*T-l*R+A*S)*B,e[1]=(n*R-r*T-s*S)*B,e[2]=(f*M-m*E+g*b)*B,e[3]=(d*E-c*M-u*b)*B,e[4]=(l*F-a*T-A*x)*B,e[5]=(i*T-n*F+s*x)*B,e[6]=(m*y-p*M-g*v)*B,e[7]=(h*M-d*y+u*v)*B,e[8]=(a*R-o*F+A*_)*B,e[9]=(r*F-i*R-s*_)*B,e[10]=(p*E-f*y+g*w)*B,e[11]=(c*y-h*E-u*w)*B,e[12]=(o*x-a*S-l*_)*B,e[13]=(i*S-r*x+n*_)*B,e[14]=(f*v-p*b-m*w)*B,e[15]=(h*b-c*v+d*w)*B}(s,s)}return function(e,t,i){return!(!e||!t||(e.pose=t,e.timestamp=t.timestamp,n(e.leftProjectionMatrix,e.leftViewMatrix,t,i._getFieldOfView("left"),i._getEyeOffset("left"),i),n(e.rightProjectionMatrix,e.rightViewMatrix,t,i._getFieldOfView("right"),i._getEyeOffset("right"),i),0))}}(),M=function(e){var t,i=e.indexOf("://");t=-1!==i?i+3:0;var r=e.indexOf("/",t);return-1===r&&(r=e.length),e.substring(0,r)},_=(e={},function(t,i){void 0===e[t]&&(console.warn("webvr-polyfill: "+i),e[t]=!0)}),x=function(e,t){var i=t?"Please use "+t+" instead.":"";_(e,e+" has been deprecated. This may not work on native WebVR displays. "+i)},F=function(e,t,i){if(t){for(var r=[],n=null,s=0;s<t.length;++s){var a=t[s];switch(a){case e.TEXTURE_BINDING_2D:case e.TEXTURE_BINDING_CUBE_MAP:var o=t[++s];if(o<e.TEXTURE0||o>e.TEXTURE31){console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"),r.push(null,null);break}n||(n=e.getParameter(e.ACTIVE_TEXTURE)),e.activeTexture(o),r.push(e.getParameter(a),null);break;case e.ACTIVE_TEXTURE:n=e.getParameter(e.ACTIVE_TEXTURE),r.push(null);break;default:r.push(e.getParameter(a))}}i(e);for(var s=0;s<t.length;++s){var a=t[s],l=r[s];switch(a){case e.ACTIVE_TEXTURE:break;case e.ARRAY_BUFFER_BINDING:e.bindBuffer(e.ARRAY_BUFFER,l);break;case e.COLOR_CLEAR_VALUE:e.clearColor(l[0],l[1],l[2],l[3]);break;case e.COLOR_WRITEMASK:e.colorMask(l[0],l[1],l[2],l[3]);break;case e.CURRENT_PROGRAM:e.useProgram(l);break;case e.ELEMENT_ARRAY_BUFFER_BINDING:e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,l);break;case e.FRAMEBUFFER_BINDING:e.bindFramebuffer(e.FRAMEBUFFER,l);break;case e.RENDERBUFFER_BINDING:e.bindRenderbuffer(e.RENDERBUFFER,l);break;case e.TEXTURE_BINDING_2D:var o=t[++s];if(o<e.TEXTURE0||o>e.TEXTURE31)break;e.activeTexture(o),e.bindTexture(e.TEXTURE_2D,l);break;case e.TEXTURE_BINDING_CUBE_MAP:var o=t[++s];if(o<e.TEXTURE0||o>e.TEXTURE31)break;e.activeTexture(o),e.bindTexture(e.TEXTURE_CUBE_MAP,l);break;case e.VIEWPORT:e.viewport(l[0],l[1],l[2],l[3]);break;case e.BLEND:case e.CULL_FACE:case e.DEPTH_TEST:case e.SCISSOR_TEST:case e.STENCIL_TEST:l?e.enable(a):e.disable(a);break;default:console.log("No GL restore behavior for 0x"+a.toString(16))}n&&e.activeTexture(n)}}else i(e)},S=["attribute vec2 position;","attribute vec3 texCoord;","varying vec2 vTexCoord;","uniform vec4 viewportOffsetScale[2];","void main() {"," vec4 viewport = viewportOffsetScale[int(texCoord.z)];"," vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;"," gl_Position = vec4( position, 1.0, 1.0 );","}"].join("\n"),R=["precision mediump float;","uniform sampler2D diffuse;","varying vec2 vTexCoord;","void main() {"," gl_FragColor = texture2D(diffuse, vTexCoord);","}"].join("\n");function T(e,t,i,r){this.gl=e,this.cardboardUI=t,this.bufferScale=i,this.dirtySubmitFrameBindings=r,this.ctxAttribs=e.getContextAttributes(),this.meshWidth=20,this.meshHeight=20,this.bufferWidth=e.drawingBufferWidth,this.bufferHeight=e.drawingBufferHeight,this.realBindFramebuffer=e.bindFramebuffer,this.realEnable=e.enable,this.realDisable=e.disable,this.realColorMask=e.colorMask,this.realClearColor=e.clearColor,this.realViewport=e.viewport,o()||(this.realCanvasWidth=Object.getOwnPropertyDescriptor(e.canvas.__proto__,"width"),this.realCanvasHeight=Object.getOwnPropertyDescriptor(e.canvas.__proto__,"height")),this.isPatched=!1,this.lastBoundFramebuffer=null,this.cullFace=!1,this.depthTest=!1,this.blend=!1,this.scissorTest=!1,this.stencilTest=!1,this.viewport=[0,0,0,0],this.colorMask=[!0,!0,!0,!0],this.clearColor=[0,0,0,0],this.attribs={position:0,texCoord:1},this.program=g(e,S,R,this.attribs),this.uniforms=w(e,this.program),this.viewportOffsetScale=new Float32Array(8),this.setTextureBounds(),this.vertexBuffer=e.createBuffer(),this.indexBuffer=e.createBuffer(),this.indexCount=0,this.renderTarget=e.createTexture(),this.framebuffer=e.createFramebuffer(),this.depthStencilBuffer=null,this.depthBuffer=null,this.stencilBuffer=null,this.ctxAttribs.depth&&this.ctxAttribs.stencil?this.depthStencilBuffer=e.createRenderbuffer():this.ctxAttribs.depth?this.depthBuffer=e.createRenderbuffer():this.ctxAttribs.stencil&&(this.stencilBuffer=e.createRenderbuffer()),this.patch(),this.onResize()}T.prototype.destroy=function(){var e=this.gl;this.unpatch(),e.deleteProgram(this.program),e.deleteBuffer(this.vertexBuffer),e.deleteBuffer(this.indexBuffer),e.deleteTexture(this.renderTarget),e.deleteFramebuffer(this.framebuffer),this.depthStencilBuffer&&e.deleteRenderbuffer(this.depthStencilBuffer),this.depthBuffer&&e.deleteRenderbuffer(this.depthBuffer),this.stencilBuffer&&e.deleteRenderbuffer(this.stencilBuffer),this.cardboardUI&&this.cardboardUI.destroy()},T.prototype.onResize=function(){var e=this.gl,t=this,i=[e.RENDERBUFFER_BINDING,e.TEXTURE_BINDING_2D,e.TEXTURE0];F(e,i,function(e){t.realBindFramebuffer.call(e,e.FRAMEBUFFER,null),t.scissorTest&&t.realDisable.call(e,e.SCISSOR_TEST),t.realColorMask.call(e,!0,!0,!0,!0),t.realViewport.call(e,0,0,e.drawingBufferWidth,e.drawingBufferHeight),t.realClearColor.call(e,0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.framebuffer),e.bindTexture(e.TEXTURE_2D,t.renderTarget),e.texImage2D(e.TEXTURE_2D,0,t.ctxAttribs.alpha?e.RGBA:e.RGB,t.bufferWidth,t.bufferHeight,0,t.ctxAttribs.alpha?e.RGBA:e.RGB,e.UNSIGNED_BYTE,null),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,t.renderTarget,0),t.ctxAttribs.depth&&t.ctxAttribs.stencil?(e.bindRenderbuffer(e.RENDERBUFFER,t.depthStencilBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.DEPTH_STENCIL,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_STENCIL_ATTACHMENT,e.RENDERBUFFER,t.depthStencilBuffer)):t.ctxAttribs.depth?(e.bindRenderbuffer(e.RENDERBUFFER,t.depthBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.DEPTH_COMPONENT16,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_ATTACHMENT,e.RENDERBUFFER,t.depthBuffer)):t.ctxAttribs.stencil&&(e.bindRenderbuffer(e.RENDERBUFFER,t.stencilBuffer),e.renderbufferStorage(e.RENDERBUFFER,e.STENCIL_INDEX8,t.bufferWidth,t.bufferHeight),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.STENCIL_ATTACHMENT,e.RENDERBUFFER,t.stencilBuffer)),!e.checkFramebufferStatus(e.FRAMEBUFFER)===e.FRAMEBUFFER_COMPLETE&&console.error("Framebuffer incomplete!"),t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.lastBoundFramebuffer),t.scissorTest&&t.realEnable.call(e,e.SCISSOR_TEST),t.realColorMask.apply(e,t.colorMask),t.realViewport.apply(e,t.viewport),t.realClearColor.apply(e,t.clearColor)}),this.cardboardUI&&this.cardboardUI.onResize()},T.prototype.patch=function(){if(!this.isPatched){var e=this,t=this.gl.canvas,i=this.gl;o()||(t.width=p()*this.bufferScale,t.height=f()*this.bufferScale,Object.defineProperty(t,"width",{configurable:!0,enumerable:!0,get:function(){return e.bufferWidth},set:function(i){e.bufferWidth=i,e.realCanvasWidth.set.call(t,i),e.onResize()}}),Object.defineProperty(t,"height",{configurable:!0,enumerable:!0,get:function(){return e.bufferHeight},set:function(i){e.bufferHeight=i,e.realCanvasHeight.set.call(t,i),e.onResize()}})),this.lastBoundFramebuffer=i.getParameter(i.FRAMEBUFFER_BINDING),null==this.lastBoundFramebuffer&&(this.lastBoundFramebuffer=this.framebuffer,this.gl.bindFramebuffer(i.FRAMEBUFFER,this.framebuffer)),this.gl.bindFramebuffer=function(t,r){e.lastBoundFramebuffer=r||e.framebuffer,e.realBindFramebuffer.call(i,t,e.lastBoundFramebuffer)},this.cullFace=i.getParameter(i.CULL_FACE),this.depthTest=i.getParameter(i.DEPTH_TEST),this.blend=i.getParameter(i.BLEND),this.scissorTest=i.getParameter(i.SCISSOR_TEST),this.stencilTest=i.getParameter(i.STENCIL_TEST),i.enable=function(t){switch(t){case i.CULL_FACE:e.cullFace=!0;break;case i.DEPTH_TEST:e.depthTest=!0;break;case i.BLEND:e.blend=!0;break;case i.SCISSOR_TEST:e.scissorTest=!0;break;case i.STENCIL_TEST:e.stencilTest=!0}e.realEnable.call(i,t)},i.disable=function(t){switch(t){case i.CULL_FACE:e.cullFace=!1;break;case i.DEPTH_TEST:e.depthTest=!1;break;case i.BLEND:e.blend=!1;break;case i.SCISSOR_TEST:e.scissorTest=!1;break;case i.STENCIL_TEST:e.stencilTest=!1}e.realDisable.call(i,t)},this.colorMask=i.getParameter(i.COLOR_WRITEMASK),i.colorMask=function(t,r,n,s){e.colorMask[0]=t,e.colorMask[1]=r,e.colorMask[2]=n,e.colorMask[3]=s,e.realColorMask.call(i,t,r,n,s)},this.clearColor=i.getParameter(i.COLOR_CLEAR_VALUE),i.clearColor=function(t,r,n,s){e.clearColor[0]=t,e.clearColor[1]=r,e.clearColor[2]=n,e.clearColor[3]=s,e.realClearColor.call(i,t,r,n,s)},this.viewport=i.getParameter(i.VIEWPORT),i.viewport=function(t,r,n,s){e.viewport[0]=t,e.viewport[1]=r,e.viewport[2]=n,e.viewport[3]=s,e.realViewport.call(i,t,r,n,s)},this.isPatched=!0,b(t)}},T.prototype.unpatch=function(){if(this.isPatched){var e=this.gl,t=this.gl.canvas;o()||(Object.defineProperty(t,"width",this.realCanvasWidth),Object.defineProperty(t,"height",this.realCanvasHeight)),t.width=this.bufferWidth,t.height=this.bufferHeight,e.bindFramebuffer=this.realBindFramebuffer,e.enable=this.realEnable,e.disable=this.realDisable,e.colorMask=this.realColorMask,e.clearColor=this.realClearColor,e.viewport=this.realViewport,this.lastBoundFramebuffer==this.framebuffer&&e.bindFramebuffer(e.FRAMEBUFFER,null),this.isPatched=!1,setTimeout(function(){b(t)},1)}},T.prototype.setTextureBounds=function(e,t){e||(e=[0,0,.5,1]),t||(t=[.5,0,.5,1]),this.viewportOffsetScale[0]=e[0],this.viewportOffsetScale[1]=e[1],this.viewportOffsetScale[2]=e[2],this.viewportOffsetScale[3]=e[3],this.viewportOffsetScale[4]=t[0],this.viewportOffsetScale[5]=t[1],this.viewportOffsetScale[6]=t[2],this.viewportOffsetScale[7]=t[3]},T.prototype.submitFrame=function(){var e=this.gl,t=this,i=[];if(this.dirtySubmitFrameBindings||i.push(e.CURRENT_PROGRAM,e.ARRAY_BUFFER_BINDING,e.ELEMENT_ARRAY_BUFFER_BINDING,e.TEXTURE_BINDING_2D,e.TEXTURE0),F(e,i,function(e){t.realBindFramebuffer.call(e,e.FRAMEBUFFER,null),t.cullFace&&t.realDisable.call(e,e.CULL_FACE),t.depthTest&&t.realDisable.call(e,e.DEPTH_TEST),t.blend&&t.realDisable.call(e,e.BLEND),t.scissorTest&&t.realDisable.call(e,e.SCISSOR_TEST),t.stencilTest&&t.realDisable.call(e,e.STENCIL_TEST),t.realColorMask.call(e,!0,!0,!0,!0),t.realViewport.call(e,0,0,e.drawingBufferWidth,e.drawingBufferHeight),(t.ctxAttribs.alpha||o())&&(t.realClearColor.call(e,0,0,0,1),e.clear(e.COLOR_BUFFER_BIT)),e.useProgram(t.program),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,t.indexBuffer),e.bindBuffer(e.ARRAY_BUFFER,t.vertexBuffer),e.enableVertexAttribArray(t.attribs.position),e.enableVertexAttribArray(t.attribs.texCoord),e.vertexAttribPointer(t.attribs.position,2,e.FLOAT,!1,20,0),e.vertexAttribPointer(t.attribs.texCoord,3,e.FLOAT,!1,20,8),e.activeTexture(e.TEXTURE0),e.uniform1i(t.uniforms.diffuse,0),e.bindTexture(e.TEXTURE_2D,t.renderTarget),e.uniform4fv(t.uniforms.viewportOffsetScale,t.viewportOffsetScale),e.drawElements(e.TRIANGLES,t.indexCount,e.UNSIGNED_SHORT,0),t.cardboardUI&&t.cardboardUI.renderNoState(),t.realBindFramebuffer.call(t.gl,e.FRAMEBUFFER,t.framebuffer),t.ctxAttribs.preserveDrawingBuffer||(t.realClearColor.call(e,0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)),t.dirtySubmitFrameBindings||t.realBindFramebuffer.call(e,e.FRAMEBUFFER,t.lastBoundFramebuffer),t.cullFace&&t.realEnable.call(e,e.CULL_FACE),t.depthTest&&t.realEnable.call(e,e.DEPTH_TEST),t.blend&&t.realEnable.call(e,e.BLEND),t.scissorTest&&t.realEnable.call(e,e.SCISSOR_TEST),t.stencilTest&&t.realEnable.call(e,e.STENCIL_TEST),t.realColorMask.apply(e,t.colorMask),t.realViewport.apply(e,t.viewport),!t.ctxAttribs.alpha&&t.ctxAttribs.preserveDrawingBuffer||t.realClearColor.apply(e,t.clearColor)}),o()){var r=e.canvas;r.width==t.bufferWidth&&r.height==t.bufferHeight||(t.bufferWidth=r.width,t.bufferHeight=r.height,t.onResize())}},T.prototype.updateDeviceInfo=function(e){var t=this.gl,i=this,r=[t.ARRAY_BUFFER_BINDING,t.ELEMENT_ARRAY_BUFFER_BINDING];F(t,r,function(t){var r=i.computeMeshVertices_(i.meshWidth,i.meshHeight,e);if(t.bindBuffer(t.ARRAY_BUFFER,i.vertexBuffer),t.bufferData(t.ARRAY_BUFFER,r,t.STATIC_DRAW),!i.indexCount){var n=i.computeMeshIndices_(i.meshWidth,i.meshHeight);t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,i.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,n,t.STATIC_DRAW),i.indexCount=n.length}})},T.prototype.computeMeshVertices_=function(e,t,i){for(var r=new Float32Array(2*e*t*5),n=i.getLeftEyeVisibleTanAngles(),s=i.getLeftEyeNoLensTanAngles(),o=i.getLeftEyeVisibleScreenRect(s),l=0,A=0;A<2;A++){for(var h=0;h<t;h++)for(var c=0;c<e;c++,l++){var d=c/(e-1),u=h/(t-1),p=d,f=u,m=a(n[0],n[2],d),g=a(n[3],n[1],u),w=Math.sqrt(m*m+g*g),v=i.distortion.distortInverse(w),y=m*v/w,b=g*v/w;d=(y-s[0])/(s[2]-s[0]),u=(b-s[3])/(s[1]-s[3]),d=2*(o.x+d*o.width-.5),u=2*(o.y+u*o.height-.5),r[5*l+0]=d,r[5*l+1]=u,r[5*l+2]=p,r[5*l+3]=f,r[5*l+4]=A}var E=n[2]-n[0];n[0]=-(E+n[0]),n[2]=E-n[2],E=s[2]-s[0],s[0]=-(E+s[0]),s[2]=E-s[2],o.x=1-(o.x+o.width)}return r},T.prototype.computeMeshIndices_=function(e,t){for(var i=new Uint16Array(2*(e-1)*(t-1)*6),r=e/2,n=t/2,s=0,a=0,o=0;o<2;o++)for(var l=0;l<t;l++)for(var A=0;A<e;A++,s++)0!=A&&0!=l&&(A<=r==l<=n?(i[a++]=s,i[a++]=s-e-1,i[a++]=s-e,i[a++]=s-e-1,i[a++]=s,i[a++]=s-1):(i[a++]=s-1,i[a++]=s-e,i[a++]=s,i[a++]=s-e,i[a++]=s-1,i[a++]=s-e-1));return i},T.prototype.getOwnPropertyDescriptor_=function(e,t){var i=Object.getOwnPropertyDescriptor(e,t);return void 0!==i.get&&void 0!==i.set||(i.configurable=!0,i.enumerable=!0,i.get=function(){return this.getAttribute(t)},i.set=function(e){this.setAttribute(t,e)}),i};var B=["attribute vec2 position;","uniform mat4 projectionMat;","void main() {"," gl_Position = projectionMat * vec4( position, -1.0, 1.0 );","}"].join("\n"),C=["precision mediump float;","uniform vec4 color;","void main() {"," gl_FragColor = color;","}"].join("\n"),P=Math.PI/180,D=.3125;function I(e){this.gl=e,this.attribs={position:0},this.program=g(e,B,C,this.attribs),this.uniforms=w(e,this.program),this.vertexBuffer=e.createBuffer(),this.gearOffset=0,this.gearVertexCount=0,this.arrowOffset=0,this.arrowVertexCount=0,this.projMat=new Float32Array(16),this.listener=null,this.onResize()}function L(e){this.coefficients=e}I.prototype.destroy=function(){var e=this.gl;this.listener&&e.canvas.removeEventListener("click",this.listener,!1),e.deleteProgram(this.program),e.deleteBuffer(this.vertexBuffer)},I.prototype.listen=function(e,t){var i=this.gl.canvas;this.listener=function(r){var n=i.clientWidth/2;r.clientX>n-42&&r.clientX<n+42&&r.clientY>i.clientHeight-42?e(r):r.clientX<42&&r.clientY<42&&t(r)},i.addEventListener("click",this.listener,!1)},I.prototype.onResize=function(){var e=this.gl,t=this,i=[e.ARRAY_BUFFER_BINDING];F(e,i,function(e){var i=[],r=e.drawingBufferWidth/2,n=Math.max(screen.width,screen.height)*window.devicePixelRatio,s=e.drawingBufferWidth/n,a=s*window.devicePixelRatio,o=4*a/2,l=42*a,A=28*a/2,h=14*a;function c(e,t){var n=(90-e)*P,s=Math.cos(n),a=Math.sin(n);i.push(D*s*A+r,D*a*A+A),i.push(t*s*A+r,t*a*A+A)}i.push(r-o,l),i.push(r-o,e.drawingBufferHeight),i.push(r+o,l),i.push(r+o,e.drawingBufferHeight),t.gearOffset=i.length/2;for(var d=0;d<=6;d++){var u=60*d;c(u,1),c(u+12,1),c(u+20,.75),c(u+40,.75),c(u+48,1)}function p(t,r){i.push(h+t,e.drawingBufferHeight-h-r)}t.gearVertexCount=i.length/2-t.gearOffset,t.arrowOffset=i.length/2;var f=o/Math.sin(45*P);p(0,A),p(A,0),p(A+f,f),p(f,A+f),p(f,A-f),p(0,A),p(A,2*A),p(A+f,2*A-f),p(f,A-f),p(0,A),p(f,A-o),p(28*a,A-o),p(f,A+o),p(28*a,A+o),t.arrowVertexCount=i.length/2-t.arrowOffset,e.bindBuffer(e.ARRAY_BUFFER,t.vertexBuffer),e.bufferData(e.ARRAY_BUFFER,new Float32Array(i),e.STATIC_DRAW)})},I.prototype.render=function(){var e=this.gl,t=this,i=[e.CULL_FACE,e.DEPTH_TEST,e.BLEND,e.SCISSOR_TEST,e.STENCIL_TEST,e.COLOR_WRITEMASK,e.VIEWPORT,e.CURRENT_PROGRAM,e.ARRAY_BUFFER_BINDING];F(e,i,function(e){e.disable(e.CULL_FACE),e.disable(e.DEPTH_TEST),e.disable(e.BLEND),e.disable(e.SCISSOR_TEST),e.disable(e.STENCIL_TEST),e.colorMask(!0,!0,!0,!0),e.viewport(0,0,e.drawingBufferWidth,e.drawingBufferHeight),t.renderNoState()})},I.prototype.renderNoState=function(){var e,t,i,r,n,s,a,o,l,A,h=this.gl;h.useProgram(this.program),h.bindBuffer(h.ARRAY_BUFFER,this.vertexBuffer),h.enableVertexAttribArray(this.attribs.position),h.vertexAttribPointer(this.attribs.position,2,h.FLOAT,!1,8,0),h.uniform4f(this.uniforms.color,1,1,1,1),e=this.projMat,t=0,i=h.drawingBufferWidth,r=0,n=h.drawingBufferHeight,o=1/(t-i),l=1/(r-n),A=1/((s=.1)-(a=1024)),e[0]=-2*o,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=-2*l,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[10]=2*A,e[11]=0,e[12]=(t+i)*o,e[13]=(n+r)*l,e[14]=(a+s)*A,e[15]=1,h.uniformMatrix4fv(this.uniforms.projectionMat,!1,this.projMat),h.drawArrays(h.TRIANGLE_STRIP,0,4),h.drawArrays(h.TRIANGLE_STRIP,this.gearOffset,this.gearVertexCount),h.drawArrays(h.TRIANGLE_STRIP,this.arrowOffset,this.arrowVertexCount)},L.prototype.distortInverse=function(e){for(var t=0,i=1,r=e-this.distort(t);Math.abs(i-t)>1e-4;){var n=e-this.distort(i),s=i-n*((i-t)/(n-r));t=i,i=s,r=n}return i},L.prototype.distort=function(e){for(var t=e*e,i=0,r=0;r<this.coefficients.length;r++)i=t*(i+this.coefficients[r]);return(i+1)*e};var O=Math.PI/180,Q=180/Math.PI,N=function(e,t,i){this.x=e||0,this.y=t||0,this.z=i||0};N.prototype={constructor:N,set:function(e,t,i){return this.x=e,this.y=t,this.z=i,this},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},normalize:function(){var e=this.length();if(0!==e){var t=1/e;this.multiplyScalar(t)}else this.x=0,this.y=0,this.z=0;return this},multiplyScalar:function(e){this.x*=e,this.y*=e,this.z*=e},applyQuaternion:function(e){var t=this.x,i=this.y,r=this.z,n=e.x,s=e.y,a=e.z,o=e.w,l=o*t+s*r-a*i,A=o*i+a*t-n*r,h=o*r+n*i-s*t,c=-n*t-s*i-a*r;return this.x=l*o+c*-n+A*-a-h*-s,this.y=A*o+c*-s+h*-n-l*-a,this.z=h*o+c*-a+l*-s-A*-n,this},dot:function(e){return this.x*e.x+this.y*e.y+this.z*e.z},crossVectors:function(e,t){var i=e.x,r=e.y,n=e.z,s=t.x,a=t.y,o=t.z;return this.x=r*o-n*a,this.y=n*s-i*o,this.z=i*a-r*s,this}};var G,k,V=function(e,t,i,r){this.x=e||0,this.y=t||0,this.z=i||0,this.w=void 0!==r?r:1};function z(e){this.width=e.width||p(),this.height=e.height||f(),this.widthMeters=e.widthMeters,this.heightMeters=e.heightMeters,this.bevelMeters=e.bevelMeters}V.prototype={constructor:V,set:function(e,t,i,r){return this.x=e,this.y=t,this.z=i,this.w=r,this},copy:function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w,this},setFromEulerXYZ:function(e,t,i){var r=Math.cos(e/2),n=Math.cos(t/2),s=Math.cos(i/2),a=Math.sin(e/2),o=Math.sin(t/2),l=Math.sin(i/2);return this.x=a*n*s+r*o*l,this.y=r*o*s-a*n*l,this.z=r*n*l+a*o*s,this.w=r*n*s-a*o*l,this},setFromEulerYXZ:function(e,t,i){var r=Math.cos(e/2),n=Math.cos(t/2),s=Math.cos(i/2),a=Math.sin(e/2),o=Math.sin(t/2),l=Math.sin(i/2);return this.x=a*n*s+r*o*l,this.y=r*o*s-a*n*l,this.z=r*n*l-a*o*s,this.w=r*n*s+a*o*l,this},setFromAxisAngle:function(e,t){var i=t/2,r=Math.sin(i);return this.x=e.x*r,this.y=e.y*r,this.z=e.z*r,this.w=Math.cos(i),this},multiply:function(e){return this.multiplyQuaternions(this,e)},multiplyQuaternions:function(e,t){var i=e.x,r=e.y,n=e.z,s=e.w,a=t.x,o=t.y,l=t.z,A=t.w;return this.x=i*A+s*a+r*l-n*o,this.y=r*A+s*o+n*a-i*l,this.z=n*A+s*l+i*o-r*a,this.w=s*A-i*a-r*o-n*l,this},inverse:function(){return this.x*=-1,this.y*=-1,this.z*=-1,this.normalize(),this},normalize:function(){var e=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);return 0===e?(this.x=0,this.y=0,this.z=0,this.w=1):(e=1/e,this.x=this.x*e,this.y=this.y*e,this.z=this.z*e,this.w=this.w*e),this},slerp:function(e,t){if(0===t)return this;if(1===t)return this.copy(e);var i=this.x,r=this.y,n=this.z,s=this.w,a=s*e.w+i*e.x+r*e.y+n*e.z;if(a<0?(this.w=-e.w,this.x=-e.x,this.y=-e.y,this.z=-e.z,a=-a):this.copy(e),a>=1)return this.w=s,this.x=i,this.y=r,this.z=n,this;var o=Math.acos(a),l=Math.sqrt(1-a*a);if(Math.abs(l)<.001)return this.w=.5*(s+this.w),this.x=.5*(i+this.x),this.y=.5*(r+this.y),this.z=.5*(n+this.z),this;var A=Math.sin((1-t)*o)/l,h=Math.sin(t*o)/l;return this.w=s*A+this.w*h,this.x=i*A+this.x*h,this.y=r*A+this.y*h,this.z=n*A+this.z*h,this},setFromUnitVectors:function(e,t){return void 0===G&&(G=new N),(k=e.dot(t)+1)<1e-6?(k=0,Math.abs(e.x)>Math.abs(e.z)?G.set(-e.y,e.x,0):G.set(0,-e.z,e.y)):G.crossVectors(e,t),this.x=G.x,this.y=G.y,this.z=G.z,this.w=k,this.normalize(),this}};var U=new z({widthMeters:.11,heightMeters:.062,bevelMeters:.004}),H=new z({widthMeters:.1038,heightMeters:.0584,bevelMeters:.004}),X={CardboardV1:new j({id:"CardboardV1",label:"Cardboard I/O 2014",fov:40,interLensDistance:.06,baselineLensDistance:.035,screenLensDistance:.042,distortionCoefficients:[.441,.156],inverseCoefficients:[-.4410035,.42756155,-.4804439,.5460139,-.58821183,.5733938,-.48303202,.33299083,-.17573841,.0651772,-.01488963,.001559834]}),CardboardV2:new j({id:"CardboardV2",label:"Cardboard I/O 2015",fov:60,interLensDistance:.064,baselineLensDistance:.035,screenLensDistance:.039,distortionCoefficients:[.34,.55],inverseCoefficients:[-.33836704,-.18162185,.862655,-1.2462051,1.0560602,-.58208317,.21609078,-.05444823,.009177956,-.0009904169,6183535e-11,-16981803e-13]})};function W(e,t){this.viewer=X.CardboardV2,this.updateDeviceParams(e),this.distortion=new L(this.viewer.distortionCoefficients);for(var i=0;i<t.length;i++){var r=t[i];X[r.id]=new j(r)}}function j(e){this.id=e.id,this.label=e.label,this.fov=e.fov,this.interLensDistance=e.interLensDistance,this.baselineLensDistance=e.baselineLensDistance,this.screenLensDistance=e.screenLensDistance,this.distortionCoefficients=e.distortionCoefficients,this.inverseCoefficients=e.inverseCoefficients}W.prototype.updateDeviceParams=function(e){this.device=this.determineDevice_(e)||this.device},W.prototype.getDevice=function(){return this.device},W.prototype.setViewer=function(e){this.viewer=e,this.distortion=new L(this.viewer.distortionCoefficients)},W.prototype.determineDevice_=function(e){if(!e)return o()?(console.warn("Using fallback iOS device measurements."),H):(console.warn("Using fallback Android device measurements."),U);var t=.0254/e.xdpi,i=.0254/e.ydpi,r=p(),n=f();return new z({widthMeters:t*r,heightMeters:i*n,bevelMeters:.001*e.bevelMm})},W.prototype.getDistortedFieldOfViewLeftEye=function(){var e=this.viewer,t=this.device,i=this.distortion,r=e.screenLensDistance,n=(t.widthMeters-e.interLensDistance)/2,s=e.interLensDistance/2,a=e.baselineLensDistance-t.bevelMeters,o=t.heightMeters-a,l=Q*Math.atan(i.distort(n/r)),A=Q*Math.atan(i.distort(s/r)),h=Q*Math.atan(i.distort(a/r)),c=Q*Math.atan(i.distort(o/r));return{leftDegrees:Math.min(l,e.fov),rightDegrees:Math.min(A,e.fov),downDegrees:Math.min(h,e.fov),upDegrees:Math.min(c,e.fov)}},W.prototype.getLeftEyeVisibleTanAngles=function(){var e=this.viewer,t=this.device,i=this.distortion,r=Math.tan(-O*e.fov),n=Math.tan(O*e.fov),s=Math.tan(O*e.fov),a=Math.tan(-O*e.fov),o=t.widthMeters/4,l=t.heightMeters/2,A=e.baselineLensDistance-t.bevelMeters-l,h=e.interLensDistance/2-o,c=-A,d=e.screenLensDistance,u=i.distort((h-o)/d),p=i.distort((c+l)/d),f=i.distort((h+o)/d),m=i.distort((c-l)/d),g=new Float32Array(4);return g[0]=Math.max(r,u),g[1]=Math.min(n,p),g[2]=Math.min(s,f),g[3]=Math.max(a,m),g},W.prototype.getLeftEyeNoLensTanAngles=function(){var e=this.viewer,t=this.device,i=this.distortion,r=new Float32Array(4),n=i.distortInverse(Math.tan(-O*e.fov)),s=i.distortInverse(Math.tan(O*e.fov)),a=i.distortInverse(Math.tan(O*e.fov)),o=i.distortInverse(Math.tan(-O*e.fov)),l=t.widthMeters/4,A=t.heightMeters/2,h=e.baselineLensDistance-t.bevelMeters-A,c=e.interLensDistance/2-l,d=-h,u=e.screenLensDistance,p=(c-l)/u,f=(d+A)/u,m=(c+l)/u,g=(d-A)/u;return r[0]=Math.max(n,p),r[1]=Math.min(s,f),r[2]=Math.min(a,m),r[3]=Math.max(o,g),r},W.prototype.getLeftEyeVisibleScreenRect=function(e){var t=this.viewer,i=this.device,r=t.screenLensDistance,n=(i.widthMeters-t.interLensDistance)/2,s=t.baselineLensDistance-i.bevelMeters,a=(e[0]*r+n)/i.widthMeters,o=(e[1]*r+s)/i.heightMeters,l=(e[2]*r+n)/i.widthMeters,A=(e[3]*r+s)/i.heightMeters;return{x:a,y:A,width:l-a,height:o-A}},W.prototype.getFieldOfViewLeftEye=function(e){return e?this.getUndistortedFieldOfViewLeftEye():this.getDistortedFieldOfViewLeftEye()},W.prototype.getFieldOfViewRightEye=function(e){var t=this.getFieldOfViewLeftEye(e);return{leftDegrees:t.rightDegrees,rightDegrees:t.leftDegrees,upDegrees:t.upDegrees,downDegrees:t.downDegrees}},W.prototype.getUndistortedFieldOfViewLeftEye=function(){var e=this.getUndistortedParams_();return{leftDegrees:Q*Math.atan(e.outerDist),rightDegrees:Q*Math.atan(e.innerDist),downDegrees:Q*Math.atan(e.bottomDist),upDegrees:Q*Math.atan(e.topDist)}},W.prototype.getUndistortedViewportLeftEye=function(){var e=this.getUndistortedParams_(),t=this.viewer,i=this.device,r=t.screenLensDistance,n=i.widthMeters/r,s=i.heightMeters/r,a=i.width/n,o=i.height/s,l=Math.round((e.eyePosX-e.outerDist)*a),A=Math.round((e.eyePosY-e.bottomDist)*o);return{x:l,y:A,width:Math.round((e.eyePosX+e.innerDist)*a)-l,height:Math.round((e.eyePosY+e.topDist)*o)-A}},W.prototype.getUndistortedParams_=function(){var e=this.viewer,t=this.device,i=this.distortion,r=e.screenLensDistance,n=e.interLensDistance/2/r,s=t.widthMeters/r,a=t.heightMeters/r,o=s/2-n,l=(e.baselineLensDistance-t.bevelMeters)/r,A=e.fov,h=i.distortInverse(Math.tan(O*A)),c=Math.min(o,h),d=Math.min(n,h),u=Math.min(l,h),p=Math.min(a-l,h);return{outerDist:c,innerDist:d,topDist:p,bottomDist:u,eyePosX:o,eyePosY:l}},W.Viewers=X;var Y={format:1,last_updated:"2018-12-10T17:01:42Z",devices:[{type:"android",rules:[{mdmh:"asus/*/Nexus 7/*"},{ua:"Nexus 7"}],dpi:[320.8,323],bw:3,ac:500},{type:"android",rules:[{mdmh:"asus/*/ASUS_Z00AD/*"},{ua:"ASUS_Z00AD"}],dpi:[403,404.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Google/*/Pixel 2 XL/*"},{ua:"Pixel 2 XL"}],dpi:537.9,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Google/*/Pixel 3 XL/*"},{ua:"Pixel 3 XL"}],dpi:[558.5,553.8],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Google/*/Pixel XL/*"},{ua:"Pixel XL"}],dpi:[537.9,533],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Google/*/Pixel 3/*"},{ua:"Pixel 3"}],dpi:442.4,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Google/*/Pixel 2/*"},{ua:"Pixel 2"}],dpi:441,bw:3,ac:500},{type:"android",rules:[{mdmh:"Google/*/Pixel/*"},{ua:"Pixel"}],dpi:[432.6,436.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"HTC/*/HTC6435LVW/*"},{ua:"HTC6435LVW"}],dpi:[449.7,443.3],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"HTC/*/HTC One XL/*"},{ua:"HTC One XL"}],dpi:[315.3,314.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"htc/*/Nexus 9/*"},{ua:"Nexus 9"}],dpi:289,bw:3,ac:500},{type:"android",rules:[{mdmh:"HTC/*/HTC One M9/*"},{ua:"HTC One M9"}],dpi:[442.5,443.3],bw:3,ac:500},{type:"android",rules:[{mdmh:"HTC/*/HTC One_M8/*"},{ua:"HTC One_M8"}],dpi:[449.7,447.4],bw:3,ac:500},{type:"android",rules:[{mdmh:"HTC/*/HTC One/*"},{ua:"HTC One"}],dpi:472.8,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Huawei/*/Nexus 6P/*"},{ua:"Nexus 6P"}],dpi:[515.1,518],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Huawei/*/BLN-L24/*"},{ua:"HONORBLN-L24"}],dpi:480,bw:4,ac:500},{type:"android",rules:[{mdmh:"Huawei/*/BKL-L09/*"},{ua:"BKL-L09"}],dpi:403,bw:3.47,ac:500},{type:"android",rules:[{mdmh:"LENOVO/*/Lenovo PB2-690Y/*"},{ua:"Lenovo PB2-690Y"}],dpi:[457.2,454.713],bw:3,ac:500},{type:"android",rules:[{mdmh:"LGE/*/Nexus 5X/*"},{ua:"Nexus 5X"}],dpi:[422,419.9],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/LGMS345/*"},{ua:"LGMS345"}],dpi:[221.7,219.1],bw:3,ac:500},{type:"android",rules:[{mdmh:"LGE/*/LG-D800/*"},{ua:"LG-D800"}],dpi:[422,424.1],bw:3,ac:500},{type:"android",rules:[{mdmh:"LGE/*/LG-D850/*"},{ua:"LG-D850"}],dpi:[537.9,541.9],bw:3,ac:500},{type:"android",rules:[{mdmh:"LGE/*/VS985 4G/*"},{ua:"VS985 4G"}],dpi:[537.9,535.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/Nexus 5/*"},{ua:"Nexus 5 B"}],dpi:[442.4,444.8],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/Nexus 4/*"},{ua:"Nexus 4"}],dpi:[319.8,318.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/LG-P769/*"},{ua:"LG-P769"}],dpi:[240.6,247.5],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/LGMS323/*"},{ua:"LGMS323"}],dpi:[206.6,204.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"LGE/*/LGLS996/*"},{ua:"LGLS996"}],dpi:[403.4,401.5],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Micromax/*/4560MMX/*"},{ua:"4560MMX"}],dpi:[240,219.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Micromax/*/A250/*"},{ua:"Micromax A250"}],dpi:[480,446.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Micromax/*/Micromax AQ4501/*"},{ua:"Micromax AQ4501"}],dpi:240,bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/G5/*"},{ua:"Moto G (5) Plus"}],dpi:[403.4,403],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/DROID RAZR/*"},{ua:"DROID RAZR"}],dpi:[368.1,256.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT830C/*"},{ua:"XT830C"}],dpi:[254,255.9],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT1021/*"},{ua:"XT1021"}],dpi:[254,256.7],bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/XT1023/*"},{ua:"XT1023"}],dpi:[254,256.7],bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/XT1028/*"},{ua:"XT1028"}],dpi:[326.6,327.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT1034/*"},{ua:"XT1034"}],dpi:[326.6,328.4],bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/XT1053/*"},{ua:"XT1053"}],dpi:[315.3,316.1],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT1562/*"},{ua:"XT1562"}],dpi:[403.4,402.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/Nexus 6/*"},{ua:"Nexus 6 B"}],dpi:[494.3,489.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT1063/*"},{ua:"XT1063"}],dpi:[295,296.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/XT1064/*"},{ua:"XT1064"}],dpi:[295,295.6],bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/XT1092/*"},{ua:"XT1092"}],dpi:[422,424.1],bw:3,ac:500},{type:"android",rules:[{mdmh:"motorola/*/XT1095/*"},{ua:"XT1095"}],dpi:[422,423.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"motorola/*/G4/*"},{ua:"Moto G (4)"}],dpi:401,bw:4,ac:1e3},{type:"android",rules:[{mdmh:"OnePlus/*/A0001/*"},{ua:"A0001"}],dpi:[403.4,401],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"OnePlus/*/ONE E1005/*"},{ua:"ONE E1005"}],dpi:[442.4,441.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"OnePlus/*/ONE A2005/*"},{ua:"ONE A2005"}],dpi:[391.9,405.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"OnePlus/*/ONEPLUS A5000/*"},{ua:"ONEPLUS A5000 "}],dpi:[403.411,399.737],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"OnePlus/*/ONE A5010/*"},{ua:"ONEPLUS A5010"}],dpi:[403,400],bw:2,ac:1e3},{type:"android",rules:[{mdmh:"OPPO/*/X909/*"},{ua:"X909"}],dpi:[442.4,444.1],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9082/*"},{ua:"GT-I9082"}],dpi:[184.7,185.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G360P/*"},{ua:"SM-G360P"}],dpi:[196.7,205.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/Nexus S/*"},{ua:"Nexus S"}],dpi:[234.5,229.8],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9300/*"},{ua:"GT-I9300"}],dpi:[304.8,303.9],bw:5,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-T230NU/*"},{ua:"SM-T230NU"}],dpi:216,bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SGH-T399/*"},{ua:"SGH-T399"}],dpi:[217.7,231.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SGH-M919/*"},{ua:"SGH-M919"}],dpi:[440.8,437.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-N9005/*"},{ua:"SM-N9005"}],dpi:[386.4,387],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SAMSUNG-SM-N900A/*"},{ua:"SAMSUNG-SM-N900A"}],dpi:[386.4,387.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9500/*"},{ua:"GT-I9500"}],dpi:[442.5,443.3],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/GT-I9505/*"},{ua:"GT-I9505"}],dpi:439.4,bw:4,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G900F/*"},{ua:"SM-G900F"}],dpi:[415.6,431.6],bw:5,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G900M/*"},{ua:"SM-G900M"}],dpi:[415.6,431.6],bw:5,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G800F/*"},{ua:"SM-G800F"}],dpi:326.8,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G906S/*"},{ua:"SM-G906S"}],dpi:[562.7,572.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9300/*"},{ua:"GT-I9300"}],dpi:[306.7,304.8],bw:5,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-T535/*"},{ua:"SM-T535"}],dpi:[142.6,136.4],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-N920C/*"},{ua:"SM-N920C"}],dpi:[515.1,518.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-N920P/*"},{ua:"SM-N920P"}],dpi:[386.3655,390.144],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-N920W8/*"},{ua:"SM-N920W8"}],dpi:[515.1,518.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9300I/*"},{ua:"GT-I9300I"}],dpi:[304.8,305.8],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-I9195/*"},{ua:"GT-I9195"}],dpi:[249.4,256.7],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SPH-L520/*"},{ua:"SPH-L520"}],dpi:[249.4,255.9],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SAMSUNG-SGH-I717/*"},{ua:"SAMSUNG-SGH-I717"}],dpi:285.8,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SPH-D710/*"},{ua:"SPH-D710"}],dpi:[217.7,204.2],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/GT-N7100/*"},{ua:"GT-N7100"}],dpi:265.1,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SCH-I605/*"},{ua:"SCH-I605"}],dpi:265.1,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/Galaxy Nexus/*"},{ua:"Galaxy Nexus"}],dpi:[315.3,314.2],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-N910H/*"},{ua:"SM-N910H"}],dpi:[515.1,518],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-N910C/*"},{ua:"SM-N910C"}],dpi:[515.2,520.2],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G130M/*"},{ua:"SM-G130M"}],dpi:[165.9,164.8],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G928I/*"},{ua:"SM-G928I"}],dpi:[515.1,518.4],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G920F/*"},{ua:"SM-G920F"}],dpi:580.6,bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G920P/*"},{ua:"SM-G920P"}],dpi:[522.5,577],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G925F/*"},{ua:"SM-G925F"}],dpi:580.6,bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G925V/*"},{ua:"SM-G925V"}],dpi:[522.5,576.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G930F/*"},{ua:"SM-G930F"}],dpi:576.6,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"samsung/*/SM-G935F/*"},{ua:"SM-G935F"}],dpi:533,bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G950F/*"},{ua:"SM-G950F"}],dpi:[562.707,565.293],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G955U/*"},{ua:"SM-G955U"}],dpi:[522.514,525.762],bw:3,ac:500},{type:"android",rules:[{mdmh:"samsung/*/SM-G955F/*"},{ua:"SM-G955F"}],dpi:[522.514,525.762],bw:3,ac:500},{type:"android",rules:[{mdmh:"Sony/*/C6903/*"},{ua:"C6903"}],dpi:[442.5,443.3],bw:3,ac:500},{type:"android",rules:[{mdmh:"Sony/*/D6653/*"},{ua:"D6653"}],dpi:[428.6,427.6],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Sony/*/E6653/*"},{ua:"E6653"}],dpi:[428.6,425.7],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Sony/*/E6853/*"},{ua:"E6853"}],dpi:[403.4,401.9],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Sony/*/SGP321/*"},{ua:"SGP321"}],dpi:[224.7,224.1],bw:3,ac:500},{type:"android",rules:[{mdmh:"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{ua:"ALCATEL ONE TOUCH Fierce"}],dpi:[240,247.5],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"THL/*/thl 5000/*"},{ua:"thl 5000"}],dpi:[480,443.3],bw:3,ac:1e3},{type:"android",rules:[{mdmh:"Fly/*/IQ4412/*"},{ua:"IQ4412"}],dpi:307.9,bw:3,ac:1e3},{type:"android",rules:[{mdmh:"ZTE/*/ZTE Blade L2/*"},{ua:"ZTE Blade L2"}],dpi:240,bw:3,ac:500},{type:"android",rules:[{mdmh:"BENEVE/*/VR518/*"},{ua:"VR518"}],dpi:480,bw:3,ac:500},{type:"ios",rules:[{res:[640,960]}],dpi:[325.1,328.4],bw:4,ac:1e3},{type:"ios",rules:[{res:[640,1136]}],dpi:[317.1,320.2],bw:3,ac:1e3},{type:"ios",rules:[{res:[750,1334]}],dpi:326.4,bw:4,ac:1e3},{type:"ios",rules:[{res:[1242,2208]}],dpi:[453.6,458.4],bw:4,ac:1e3},{type:"ios",rules:[{res:[1125,2001]}],dpi:[410.9,415.4],bw:4,ac:1e3},{type:"ios",rules:[{res:[1125,2436]}],dpi:458,bw:4,ac:1e3}]};function Z(e,t){if(this.dpdb=Y,this.recalculateDeviceParams_(),e){this.onDeviceParamsUpdated=t;var i=new XMLHttpRequest,r=this;i.open("GET",e,!0),i.addEventListener("load",function(){r.loading=!1,i.status>=200&&i.status<=299?(r.dpdb=JSON.parse(i.response),r.recalculateDeviceParams_()):console.error("Error loading online DPDB!")}),i.send()}}function q(e){this.xdpi=e.xdpi,this.ydpi=e.ydpi,this.bevelMm=e.bevelMm}function J(e,t){this.set(e,t)}function K(e,t){this.kFilter=e,this.isDebug=t,this.currentAccelMeasurement=new J,this.currentGyroMeasurement=new J,this.previousGyroMeasurement=new J,o()?this.filterQ=new V(-1,0,0,1):this.filterQ=new V(1,0,0,1),this.previousFilterQ=new V,this.previousFilterQ.copy(this.filterQ),this.accelQ=new V,this.isOrientationInitialized=!1,this.estimatedGravity=new N,this.measuredGravity=new N,this.gyroIntegralQ=new V}function $(e,t){this.predictionTimeS=e,this.isDebug=t,this.previousQ=new V,this.previousTimestampS=null,this.deltaQ=new V,this.outQ=new V}function ee(e,t,i,r){this.yawOnly=i,this.accelerometer=new N,this.gyroscope=new N,this.filter=new K(e,r),this.posePredictor=new $(t,r),this.isFirefoxAndroid=A(),this.isIOS=o();var n=h();this.isDeviceMotionInRadians=!this.isIOS&&n&&n<66,this.isWithoutDeviceMotion=c(),this.filterToWorldQ=new V,o()?this.filterToWorldQ.setFromAxisAngle(new N(1,0,0),Math.PI/2):this.filterToWorldQ.setFromAxisAngle(new N(1,0,0),-Math.PI/2),this.inverseWorldToScreenQ=new V,this.worldToScreenQ=new V,this.originalPoseAdjustQ=new V,this.originalPoseAdjustQ.setFromAxisAngle(new N(0,0,1),-window.orientation*Math.PI/180),this.setScreenTransform_(),u()&&this.filterToWorldQ.multiply(this.inverseWorldToScreenQ),this.resetQ=new V,this.orientationOut_=new Float32Array(4),this.start()}Z.prototype.getDeviceParams=function(){return this.deviceParams},Z.prototype.recalculateDeviceParams_=function(){var e=this.calcDeviceParams_();e?(this.deviceParams=e,this.onDeviceParamsUpdated&&this.onDeviceParamsUpdated(this.deviceParams)):console.error("Failed to recalculate device parameters.")},Z.prototype.calcDeviceParams_=function(){var e=this.dpdb;if(!e)return console.error("DPDB not available."),null;if(1!=e.format)return console.error("DPDB has unexpected format version."),null;if(!e.devices||!e.devices.length)return console.error("DPDB does not have a devices section."),null;var t=navigator.userAgent||navigator.vendor||window.opera,i=p(),r=f();if(!e.devices)return console.error("DPDB has no devices section."),null;for(var n=0;n<e.devices.length;n++){var s=e.devices[n];if(s.rules)if("ios"==s.type||"android"==s.type){if(o()==("ios"==s.type)){for(var a=!1,l=0;l<s.rules.length;l++){var A=s.rules[l];if(this.ruleMatches_(A,t,i,r)){a=!0;break}}if(a){var h=s.dpi[0]||s.dpi,c=s.dpi[1]||s.dpi;return new q({xdpi:h,ydpi:c,bevelMm:s.bw})}}}else console.warn("Device["+n+"] has invalid type.");else console.warn("Device["+n+"] has no rules section.")}return console.warn("No DPDB device match."),null},Z.prototype.ruleMatches_=function(e,t,i,r){if(!e.ua&&!e.res)return!1;if(e.ua&&"SM"===e.ua.substring(0,2)&&(e.ua=e.ua.substring(0,7)),e.ua&&t.indexOf(e.ua)<0)return!1;if(e.res){if(!e.res[0]||!e.res[1])return!1;var n=e.res[0],s=e.res[1];if(Math.min(i,r)!=Math.min(n,s)||Math.max(i,r)!=Math.max(n,s))return!1}return!0},J.prototype.set=function(e,t){this.sample=e,this.timestampS=t},J.prototype.copy=function(e){this.set(e.sample,e.timestampS)},K.prototype.addAccelMeasurement=function(e,t){this.currentAccelMeasurement.set(e,t)},K.prototype.addGyroMeasurement=function(e,t){this.currentGyroMeasurement.set(e,t);var i,r=t-this.previousGyroMeasurement.timestampS;i=r,!(isNaN(i)||i<=.001||i>1)&&this.run_(),this.previousGyroMeasurement.copy(this.currentGyroMeasurement)},K.prototype.run_=function(){if(!this.isOrientationInitialized)return this.accelQ=this.accelToQuaternion_(this.currentAccelMeasurement.sample),this.previousFilterQ.copy(this.accelQ),void(this.isOrientationInitialized=!0);var e=this.currentGyroMeasurement.timestampS-this.previousGyroMeasurement.timestampS,t=this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample,e);this.gyroIntegralQ.multiply(t),this.filterQ.copy(this.previousFilterQ),this.filterQ.multiply(t);var i=new V;i.copy(this.filterQ),i.inverse(),this.estimatedGravity.set(0,0,-1),this.estimatedGravity.applyQuaternion(i),this.estimatedGravity.normalize(),this.measuredGravity.copy(this.currentAccelMeasurement.sample),this.measuredGravity.normalize();var r,n=new V;n.setFromUnitVectors(this.estimatedGravity,this.measuredGravity),n.inverse(),this.isDebug&&console.log("Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)",Q*((r=n).w>1?(console.warn("getQuaternionAngle: w > 1"),0):2*Math.acos(r.w)),this.estimatedGravity.x.toFixed(1),this.estimatedGravity.y.toFixed(1),this.estimatedGravity.z.toFixed(1),this.measuredGravity.x.toFixed(1),this.measuredGravity.y.toFixed(1),this.measuredGravity.z.toFixed(1));var s=new V;s.copy(this.filterQ),s.multiply(n),this.filterQ.slerp(s,1-this.kFilter),this.previousFilterQ.copy(this.filterQ)},K.prototype.getOrientation=function(){return this.filterQ},K.prototype.accelToQuaternion_=function(e){var t=new N;t.copy(e),t.normalize();var i=new V;return i.setFromUnitVectors(new N(0,0,-1),t),i.inverse(),i},K.prototype.gyroToQuaternionDelta_=function(e,t){var i=new V,r=new N;return r.copy(e),r.normalize(),i.setFromAxisAngle(r,e.length()*t),i},$.prototype.getPrediction=function(e,t,i){if(!this.previousTimestampS)return this.previousQ.copy(e),this.previousTimestampS=i,e;var r=new N;r.copy(t),r.normalize();var n=t.length();if(n<20*O)return this.isDebug&&console.log("Moving slowly, at %s deg/s: no prediction",(Q*n).toFixed(1)),this.outQ.copy(e),this.previousQ.copy(e),this.outQ;var s=n*this.predictionTimeS;return this.deltaQ.setFromAxisAngle(r,s),this.outQ.copy(this.previousQ),this.outQ.multiply(this.deltaQ),this.previousQ.copy(e),this.previousTimestampS=i,this.outQ},ee.prototype.getPosition=function(){return null},ee.prototype.getOrientation=function(){var e=void 0;if(this.isWithoutDeviceMotion&&this._deviceOrientationQ){this.deviceOrientationFixQ=this.deviceOrientationFixQ||(r=(new V).setFromAxisAngle(new N(0,0,-1),0),n=new V,-90===window.orientation?n.setFromAxisAngle(new N(0,1,0),Math.PI/-2):n.setFromAxisAngle(new N(0,1,0),Math.PI/2),r.multiply(n)),this.deviceOrientationFilterToWorldQ=this.deviceOrientationFilterToWorldQ||((i=new V).setFromAxisAngle(new N(1,0,0),-Math.PI/2),i),e=this._deviceOrientationQ;var t=new V;return t.copy(e),t.multiply(this.deviceOrientationFilterToWorldQ),t.multiply(this.resetQ),t.multiply(this.worldToScreenQ),t.multiplyQuaternions(this.deviceOrientationFixQ,t),this.yawOnly&&(t.x=0,t.z=0,t.normalize()),this.orientationOut_[0]=t.x,this.orientationOut_[1]=t.y,this.orientationOut_[2]=t.z,this.orientationOut_[3]=t.w,this.orientationOut_}var i,r,n,s=this.filter.getOrientation();e=this.posePredictor.getPrediction(s,this.gyroscope,this.previousTimestampS);var t=new V;return t.copy(this.filterToWorldQ),t.multiply(this.resetQ),t.multiply(e),t.multiply(this.worldToScreenQ),this.yawOnly&&(t.x=0,t.z=0,t.normalize()),this.orientationOut_[0]=t.x,this.orientationOut_[1]=t.y,this.orientationOut_[2]=t.z,this.orientationOut_[3]=t.w,this.orientationOut_},ee.prototype.resetPose=function(){this.resetQ.copy(this.filter.getOrientation()),this.resetQ.x=0,this.resetQ.y=0,this.resetQ.z*=-1,this.resetQ.normalize(),u()&&this.resetQ.multiply(this.inverseWorldToScreenQ),this.resetQ.multiply(this.originalPoseAdjustQ)},ee.prototype.onDeviceOrientation_=function(e){this._deviceOrientationQ=this._deviceOrientationQ||new V;var t=e.alpha,i=e.beta,r=e.gamma;t=(t||0)*Math.PI/180,i=(i||0)*Math.PI/180,r=(r||0)*Math.PI/180,this._deviceOrientationQ.setFromEulerYXZ(i,t,-r)},ee.prototype.onDeviceMotion_=function(e){this.updateDeviceMotion_(e)},ee.prototype.updateDeviceMotion_=function(e){var t=e.accelerationIncludingGravity,i=e.rotationRate,r=e.timeStamp/1e3,n=r-this.previousTimestampS;return n<0?(_("fusion-pose-sensor:invalid:non-monotonic","Invalid timestamps detected: non-monotonic timestamp from devicemotion"),void(this.previousTimestampS=r)):n<=.001||n>1?(_("fusion-pose-sensor:invalid:outside-threshold","Invalid timestamps detected: Timestamp from devicemotion outside expected range."),void(this.previousTimestampS=r)):(this.accelerometer.set(-t.x,-t.y,-t.z),d()?this.gyroscope.set(-i.beta,i.alpha,i.gamma):this.gyroscope.set(i.alpha,i.beta,i.gamma),this.isDeviceMotionInRadians||this.gyroscope.multiplyScalar(Math.PI/180),this.filter.addAccelMeasurement(this.accelerometer,r),this.filter.addGyroMeasurement(this.gyroscope,r),void(this.previousTimestampS=r))},ee.prototype.onOrientationChange_=function(e){this.setScreenTransform_()},ee.prototype.onMessage_=function(e){var t=e.data;if(t&&t.type){var i=t.type.toLowerCase();"devicemotion"===i&&this.updateDeviceMotion_(t.deviceMotionEvent)}},ee.prototype.setScreenTransform_=function(){switch(this.worldToScreenQ.set(0,0,0,1),window.orientation){case 0:break;case 90:this.worldToScreenQ.setFromAxisAngle(new N(0,0,1),-Math.PI/2);break;case-90:this.worldToScreenQ.setFromAxisAngle(new N(0,0,1),Math.PI/2)}this.inverseWorldToScreenQ.copy(this.worldToScreenQ),this.inverseWorldToScreenQ.inverse()},ee.prototype.start=function(){var e,t,i;this.onDeviceMotionCallback_=this.onDeviceMotion_.bind(this),this.onOrientationChangeCallback_=this.onOrientationChange_.bind(this),this.onMessageCallback_=this.onMessage_.bind(this),this.onDeviceOrientationCallback_=this.onDeviceOrientation_.bind(this),o()&&(e=window.self!==window.top,t=M(document.referrer),i=M(window.location.href),e&&t!==i)&&window.addEventListener("message",this.onMessageCallback_),window.addEventListener("orientationchange",this.onOrientationChangeCallback_),this.isWithoutDeviceMotion?window.addEventListener("deviceorientation",this.onDeviceOrientationCallback_):window.addEventListener("devicemotion",this.onDeviceMotionCallback_)},ee.prototype.stop=function(){window.removeEventListener("devicemotion",this.onDeviceMotionCallback_),window.removeEventListener("deviceorientation",this.onDeviceOrientationCallback_),window.removeEventListener("orientationchange",this.onOrientationChangeCallback_),window.removeEventListener("message",this.onMessageCallback_)};var te=new N(1,0,0),ie=new N(0,0,1),re=new V;re.setFromAxisAngle(te,-Math.PI/2),re.multiply((new V).setFromAxisAngle(ie,Math.PI/2));var ne=function(){function e(t){r(this,e),this.config=t,this.sensor=null,this.fusionSensor=null,this._out=new Float32Array(4),this.api=null,this.errors=[],this._sensorQ=new V,this._outQ=new V,this._onSensorRead=this._onSensorRead.bind(this),this._onSensorError=this._onSensorError.bind(this),this.init()}return n(e,[{key:"init",value:function(){var e=null;try{(e=new RelativeOrientationSensor({frequency:60,referenceFrame:"screen"})).addEventListener("error",this._onSensorError)}catch(e){this.errors.push(e),"SecurityError"===e.name?(console.error("Cannot construct sensors due to the Feature Policy"),console.warn('Attempting to fall back using "devicemotion"; however this will fail in the future without correct permissions.'),this.useDeviceMotion()):"ReferenceError"===e.name?this.useDeviceMotion():console.error(e)}e&&(this.api="sensor",this.sensor=e,this.sensor.addEventListener("reading",this._onSensorRead),this.sensor.start())}},{key:"useDeviceMotion",value:function(){this.api="devicemotion",this.fusionSensor=new ee(this.config.K_FILTER,this.config.PREDICTION_TIME_S,this.config.YAW_ONLY,this.config.DEBUG),this.sensor&&(this.sensor.removeEventListener("reading",this._onSensorRead),this.sensor.removeEventListener("error",this._onSensorError),this.sensor=null)}},{key:"getOrientation",value:function(){if(this.fusionSensor)return this.fusionSensor.getOrientation();if(!this.sensor||!this.sensor.quaternion)return this._out[0]=this._out[1]=this._out[2]=0,this._out[3]=1,this._out;var e=this.sensor.quaternion;this._sensorQ.set(e[0],e[1],e[2],e[3]);var t=this._outQ;return t.copy(re),t.multiply(this._sensorQ),this.config.YAW_ONLY&&(t.x=t.z=0,t.normalize()),this._out[0]=t.x,this._out[1]=t.y,this._out[2]=t.z,this._out[3]=t.w,this._out}},{key:"_onSensorError",value:function(e){this.errors.push(e.error),"NotAllowedError"===e.error.name?console.error("Permission to access sensor was denied"):"NotReadableError"===e.error.name?console.error("Sensor could not be read"):console.error(e.error),this.useDeviceMotion()}},{key:"_onSensorRead",value:function(){}}]),e}();function se(){this.loadIcon_();var e=document.createElement("div"),t=e.style;t.position="fixed",t.top=0,t.right=0,t.bottom=0,t.left=0,t.backgroundColor="gray",t.fontFamily="sans-serif",t.zIndex=1e6;var i=document.createElement("img");i.src=this.icon;var t=i.style;t.marginLeft="25%",t.marginTop="25%",t.width="50%",e.appendChild(i);var r=document.createElement("div"),t=r.style;t.textAlign="center",t.fontSize="16px",t.lineHeight="24px",t.margin="24px 25%",t.width="50%",r.innerHTML="Place your phone into your Cardboard viewer.",e.appendChild(r);var n=document.createElement("div"),t=n.style;t.backgroundColor="#CFD8DC",t.position="fixed",t.bottom=0,t.width="100%",t.height="48px",t.padding="14px 24px",t.boxSizing="border-box",t.color="#656A6B",e.appendChild(n);var s=document.createElement("div");s.style.float="left",s.innerHTML="No Cardboard viewer?";var a=document.createElement("a");a.href="https://www.google.com/get/cardboard/get-cardboard/",a.innerHTML="get one",a.target="_blank";var t=a.style;t.float="right",t.fontWeight=600,t.textTransform="uppercase",t.borderLeft="1px solid gray",t.paddingLeft="24px",t.textDecoration="none",t.color="#656A6B",n.appendChild(s),n.appendChild(a),this.overlay=e,this.text=r,this.hide()}se.prototype.show=function(e){e||this.overlay.parentElement?e&&(this.overlay.parentElement&&this.overlay.parentElement!=e&&this.overlay.parentElement.removeChild(this.overlay),e.appendChild(this.overlay)):document.body.appendChild(this.overlay),this.overlay.style.display="block";var t=this.overlay.querySelector("img"),i=t.style;u()?(i.width="20%",i.marginLeft="40%",i.marginTop="3%"):(i.width="50%",i.marginLeft="25%",i.marginTop="25%")},se.prototype.hide=function(){this.overlay.style.display="none"},se.prototype.showTemporarily=function(e,t){this.show(t),this.timer=setTimeout(this.hide.bind(this),e)},se.prototype.disableShowTemporarily=function(){clearTimeout(this.timer)},se.prototype.update=function(){this.disableShowTemporarily(),!u()&&v()?this.show():this.hide()},se.prototype.loadIcon_=function(){this.icon="data:image/svg+xml,"+encodeURIComponent("<svg width='198' height='240' viewBox='0 0 198 240' xmlns='http://www.w3.org/2000/svg'><g fill='none' fill-rule='evenodd'><path d='M149.625 109.527l6.737 3.891v.886c0 .177.013.36.038.549.01.081.02.162.027.242.14 1.415.974 2.998 2.105 3.999l5.72 5.062.081-.09s4.382-2.53 5.235-3.024l25.97 14.993v54.001c0 .771-.386 1.217-.948 1.217-.233 0-.495-.076-.772-.236l-23.967-13.838-.014.024-27.322 15.775-.85-1.323c-4.731-1.529-9.748-2.74-14.951-3.61a.27.27 0 0 0-.007.024l-5.067 16.961-7.891 4.556-.037-.063v27.59c0 .772-.386 1.217-.948 1.217-.232 0-.495-.076-.772-.236l-42.473-24.522c-.95-.549-1.72-1.877-1.72-2.967v-1.035l-.021.047a5.111 5.111 0 0 0-1.816-.399 5.682 5.682 0 0 0-.546.001 13.724 13.724 0 0 1-1.918-.041c-1.655-.153-3.2-.6-4.404-1.296l-46.576-26.89.005.012-10.278-18.75c-1.001-1.827-.241-4.216 1.698-5.336l56.011-32.345a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.659 3.227 1.853l.005-.003.227.413-.006.004a9.63 9.63 0 0 0 1.477 2.018l.277.27c1.914 1.85 4.468 2.801 7.113 2.801 1.949 0 3.948-.517 5.775-1.572.013 0 7.319-4.219 7.319-4.219a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.658 3.226 1.853l3.25 5.928.022-.018 6.785 3.917-.105-.182 46.881-26.965m0-1.635c-.282 0-.563.073-.815.218l-46.169 26.556-5.41-3.124-3.005-5.481c-.913-1.667-2.699-2.702-4.66-2.703-1.011 0-2.02.274-2.917.792a3825 3825 0 0 1-7.275 4.195l-.044.024a9.937 9.937 0 0 1-4.957 1.353c-2.292 0-4.414-.832-5.976-2.342l-.252-.245a7.992 7.992 0 0 1-1.139-1.534 1.379 1.379 0 0 0-.06-.122l-.227-.414a1.718 1.718 0 0 0-.095-.154c-.938-1.574-2.673-2.545-4.571-2.545-1.011 0-2.02.274-2.917.792L3.125 155.502c-2.699 1.559-3.738 4.94-2.314 7.538l10.278 18.75c.177.323.448.563.761.704l46.426 26.804c1.403.81 3.157 1.332 5.072 1.508a15.661 15.661 0 0 0 2.146.046 4.766 4.766 0 0 1 .396 0c.096.004.19.011.283.022.109 1.593 1.159 3.323 2.529 4.114l42.472 24.522c.524.302 1.058.455 1.59.455 1.497 0 2.583-1.2 2.583-2.852v-26.562l7.111-4.105a1.64 1.64 0 0 0 .749-.948l4.658-15.593c4.414.797 8.692 1.848 12.742 3.128l.533.829a1.634 1.634 0 0 0 2.193.531l26.532-15.317L193 192.433c.523.302 1.058.455 1.59.455 1.497 0 2.583-1.199 2.583-2.852v-54.001c0-.584-.312-1.124-.818-1.416l-25.97-14.993a1.633 1.633 0 0 0-1.636.001c-.606.351-2.993 1.73-4.325 2.498l-4.809-4.255c-.819-.725-1.461-1.933-1.561-2.936a7.776 7.776 0 0 0-.033-.294 2.487 2.487 0 0 1-.023-.336v-.886c0-.584-.312-1.123-.817-1.416l-6.739-3.891a1.633 1.633 0 0 0-.817-.219' fill='#455A64'/><path d='M96.027 132.636l46.576 26.891c1.204.695 1.979 1.587 2.242 2.541l-.01.007-81.374 46.982h-.001c-1.654-.152-3.199-.6-4.403-1.295l-46.576-26.891 83.546-48.235' fill='#FAFAFA'/><path d='M63.461 209.174c-.008 0-.015 0-.022-.002-1.693-.156-3.228-.609-4.441-1.309l-46.576-26.89a.118.118 0 0 1 0-.203l83.546-48.235a.117.117 0 0 1 .117 0l46.576 26.891c1.227.708 2.021 1.612 2.296 2.611a.116.116 0 0 1-.042.124l-.021.016-81.375 46.981a.11.11 0 0 1-.058.016zm-50.747-28.303l46.401 26.79c1.178.68 2.671 1.121 4.32 1.276l81.272-46.922c-.279-.907-1.025-1.73-2.163-2.387l-46.517-26.857-83.313 48.1z' fill='#607D8B'/><path d='M148.327 165.471a5.85 5.85 0 0 1-.546.001c-1.894-.083-3.302-1.038-3.145-2.132a2.693 2.693 0 0 0-.072-1.105l-81.103 46.822c.628.058 1.272.073 1.918.042.182-.009.364-.009.546-.001 1.894.083 3.302 1.038 3.145 2.132l79.257-45.759' fill='#FFF'/><path d='M69.07 211.347a.118.118 0 0 1-.115-.134c.045-.317-.057-.637-.297-.925-.505-.61-1.555-1.022-2.738-1.074a5.966 5.966 0 0 0-.535.001 14.03 14.03 0 0 1-1.935-.041.117.117 0 0 1-.103-.092.116.116 0 0 1 .055-.126l81.104-46.822a.117.117 0 0 1 .171.07c.104.381.129.768.074 1.153-.045.316.057.637.296.925.506.61 1.555 1.021 2.739 1.073.178.008.357.008.535-.001a.117.117 0 0 1 .064.218l-79.256 45.759a.114.114 0 0 1-.059.016zm-3.405-2.372c.089 0 .177.002.265.006 1.266.056 2.353.488 2.908 1.158.227.274.35.575.36.882l78.685-45.429c-.036 0-.072-.001-.107-.003-1.267-.056-2.354-.489-2.909-1.158-.282-.34-.402-.724-.347-1.107a2.604 2.604 0 0 0-.032-.91L63.846 208.97a13.91 13.91 0 0 0 1.528.012c.097-.005.194-.007.291-.007z' fill='#607D8B'/><path d='M2.208 162.134c-1.001-1.827-.241-4.217 1.698-5.337l56.011-32.344c1.939-1.12 4.324-.546 5.326 1.281l.232.41a9.344 9.344 0 0 0 1.47 2.021l.278.27c3.325 3.214 8.583 3.716 12.888 1.23l7.319-4.22c1.94-1.119 4.324-.546 5.325 1.282l3.25 5.928-83.519 48.229-10.278-18.75z' fill='#FAFAFA'/><path d='M12.486 181.001a.112.112 0 0 1-.031-.005.114.114 0 0 1-.071-.056L2.106 162.19c-1.031-1.88-.249-4.345 1.742-5.494l56.01-32.344a4.328 4.328 0 0 1 2.158-.588c1.415 0 2.65.702 3.311 1.882.01.008.018.017.024.028l.227.414a.122.122 0 0 1 .013.038 9.508 9.508 0 0 0 1.439 1.959l.275.266c1.846 1.786 4.344 2.769 7.031 2.769 1.977 0 3.954-.538 5.717-1.557a.148.148 0 0 1 .035-.013l7.284-4.206a4.321 4.321 0 0 1 2.157-.588c1.427 0 2.672.716 3.329 1.914l3.249 5.929a.116.116 0 0 1-.044.157l-83.518 48.229a.116.116 0 0 1-.059.016zm49.53-57.004c-.704 0-1.41.193-2.041.557l-56.01 32.345c-1.882 1.086-2.624 3.409-1.655 5.179l10.221 18.645 83.317-48.112-3.195-5.829c-.615-1.122-1.783-1.792-3.124-1.792a4.08 4.08 0 0 0-2.04.557l-7.317 4.225a.148.148 0 0 1-.035.013 11.7 11.7 0 0 1-5.801 1.569c-2.748 0-5.303-1.007-7.194-2.835l-.278-.27a9.716 9.716 0 0 1-1.497-2.046.096.096 0 0 1-.013-.037l-.191-.347a.11.11 0 0 1-.023-.029c-.615-1.123-1.783-1.793-3.124-1.793z' fill='#607D8B'/><path d='M42.434 155.808c-2.51-.001-4.697-1.258-5.852-3.365-1.811-3.304-.438-7.634 3.059-9.654l12.291-7.098a7.599 7.599 0 0 1 3.789-1.033c2.51 0 4.697 1.258 5.852 3.365 1.811 3.304.439 7.634-3.059 9.654l-12.291 7.098a7.606 7.606 0 0 1-3.789 1.033zm13.287-20.683a7.128 7.128 0 0 0-3.555.971l-12.291 7.098c-3.279 1.893-4.573 5.942-2.883 9.024 1.071 1.955 3.106 3.122 5.442 3.122a7.13 7.13 0 0 0 3.556-.97l12.291-7.098c3.279-1.893 4.572-5.942 2.883-9.024-1.072-1.955-3.106-3.123-5.443-3.123z' fill='#607D8B'/><path d='M149.588 109.407l6.737 3.89v.887c0 .176.013.36.037.549.011.081.02.161.028.242.14 1.415.973 2.998 2.105 3.999l7.396 6.545c.177.156.358.295.541.415 1.579 1.04 2.95.466 3.062-1.282.049-.784.057-1.595.023-2.429l-.003-.16v-1.151l25.987 15.003v54c0 1.09-.77 1.53-1.72.982l-42.473-24.523c-.95-.548-1.72-1.877-1.72-2.966v-34.033' fill='#FAFAFA'/><path d='M194.553 191.25c-.257 0-.54-.085-.831-.253l-42.472-24.521c-.981-.567-1.779-1.943-1.779-3.068v-34.033h.234v34.033c0 1.051.745 2.336 1.661 2.866l42.473 24.521c.424.245.816.288 1.103.122.285-.164.442-.52.442-1.002v-53.933l-25.753-14.868.003 1.106c.034.832.026 1.654-.024 2.439-.054.844-.396 1.464-.963 1.746-.619.309-1.45.173-2.28-.373a5.023 5.023 0 0 1-.553-.426l-7.397-6.544c-1.158-1.026-1.999-2.625-2.143-4.076a9.624 9.624 0 0 0-.027-.238 4.241 4.241 0 0 1-.038-.564v-.82l-6.68-3.856.117-.202 6.738 3.89.058.034v.954c0 .171.012.351.036.533.011.083.021.165.029.246.138 1.395.948 2.935 2.065 3.923l7.397 6.545c.173.153.35.289.527.406.758.499 1.504.63 2.047.359.49-.243.786-.795.834-1.551.05-.778.057-1.591.024-2.417l-.004-.163v-1.355l.175.1 25.987 15.004.059.033v54.068c0 .569-.198.996-.559 1.204a1.002 1.002 0 0 1-.506.131' fill='#607D8B'/><path d='M145.685 163.161l24.115 13.922-25.978 14.998-1.462-.307c-6.534-2.17-13.628-3.728-21.019-4.616-4.365-.524-8.663 1.096-9.598 3.62a2.746 2.746 0 0 0-.011 1.928c1.538 4.267 4.236 8.363 7.995 12.135l.532.845-25.977 14.997-24.115-13.922 75.518-43.6' fill='#FFF'/><path d='M94.282 220.818l-.059-.033-24.29-14.024.175-.101 75.577-43.634.058.033 24.29 14.024-26.191 15.122-.045-.01-1.461-.307c-6.549-2.174-13.613-3.725-21.009-4.614a13.744 13.744 0 0 0-1.638-.097c-3.758 0-7.054 1.531-7.837 3.642a2.62 2.62 0 0 0-.01 1.848c1.535 4.258 4.216 8.326 7.968 12.091l.016.021.526.835.006.01.064.102-.105.061-25.977 14.998-.058.033zm-23.881-14.057l23.881 13.788 24.802-14.32c.546-.315.846-.489 1.017-.575l-.466-.74c-3.771-3.787-6.467-7.881-8.013-12.168a2.851 2.851 0 0 1 .011-2.008c.815-2.199 4.203-3.795 8.056-3.795.557 0 1.117.033 1.666.099 7.412.891 14.491 2.445 21.041 4.621.836.175 1.215.254 1.39.304l25.78-14.884-23.881-13.788-75.284 43.466z' fill='#607D8B'/><path d='M167.23 125.979v50.871l-27.321 15.773-6.461-14.167c-.91-1.996-3.428-1.738-5.624.574a10.238 10.238 0 0 0-2.33 4.018l-6.46 21.628-27.322 15.774v-50.871l75.518-43.6' fill='#FFF'/><path d='M91.712 220.567a.127.127 0 0 1-.059-.016.118.118 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.519-43.6a.117.117 0 0 1 .175.101v50.871c0 .041-.023.08-.059.1l-27.321 15.775a.118.118 0 0 1-.094.01.12.12 0 0 1-.071-.063l-6.46-14.168c-.375-.822-1.062-1.275-1.934-1.275-1.089 0-2.364.686-3.5 1.881a10.206 10.206 0 0 0-2.302 3.972l-6.46 21.627a.118.118 0 0 1-.054.068L91.77 220.551a.12.12 0 0 1-.058.016zm.117-50.92v50.601l27.106-15.65 6.447-21.583a10.286 10.286 0 0 1 2.357-4.065c1.18-1.242 2.517-1.954 3.669-1.954.969 0 1.731.501 2.146 1.411l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M168.543 126.213v50.87l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.624.574a10.248 10.248 0 0 0-2.33 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6' fill='#FFF'/><path d='M93.025 220.8a.123.123 0 0 1-.059-.015.12.12 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.518-43.6a.112.112 0 0 1 .117 0c.036.02.059.059.059.1v50.871a.116.116 0 0 1-.059.101l-27.321 15.774a.111.111 0 0 1-.094.01.115.115 0 0 1-.071-.062l-6.46-14.168c-.375-.823-1.062-1.275-1.935-1.275-1.088 0-2.363.685-3.499 1.881a10.19 10.19 0 0 0-2.302 3.971l-6.461 21.628a.108.108 0 0 1-.053.067l-27.322 15.775a.12.12 0 0 1-.058.015zm.117-50.919v50.6l27.106-15.649 6.447-21.584a10.293 10.293 0 0 1 2.357-4.065c1.179-1.241 2.516-1.954 3.668-1.954.969 0 1.732.502 2.147 1.412l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M169.8 177.083l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.625.574a10.246 10.246 0 0 0-2.329 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6v50.87z' fill='#FAFAFA'/><path d='M94.282 220.917a.234.234 0 0 1-.234-.233v-50.871c0-.083.045-.161.117-.202l75.518-43.601a.234.234 0 1 1 .35.202v50.871a.233.233 0 0 1-.116.202l-27.322 15.775a.232.232 0 0 1-.329-.106l-6.461-14.168c-.36-.789-.992-1.206-1.828-1.206-1.056 0-2.301.672-3.415 1.844a10.099 10.099 0 0 0-2.275 3.924l-6.46 21.628a.235.235 0 0 1-.107.136l-27.322 15.774a.23.23 0 0 1-.116.031zm.233-50.969v50.331l26.891-15.525 6.434-21.539a10.41 10.41 0 0 1 2.384-4.112c1.201-1.265 2.569-1.991 3.753-1.991 1.018 0 1.818.526 2.253 1.48l6.354 13.934 26.982-15.578v-50.331l-75.051 43.331z' fill='#607D8B'/><path d='M109.894 199.943c-1.774 0-3.241-.725-4.244-2.12a.224.224 0 0 1 .023-.294.233.233 0 0 1 .301-.023c.78.547 1.705.827 2.75.827 1.323 0 2.754-.439 4.256-1.306 5.311-3.067 9.631-10.518 9.631-16.611 0-1.927-.442-3.56-1.278-4.724a.232.232 0 0 1 .323-.327c1.671 1.172 2.591 3.381 2.591 6.219 0 6.242-4.426 13.863-9.865 17.003-1.574.908-3.084 1.356-4.488 1.356zm-2.969-1.542c.813.651 1.82.877 2.968.877h.001c1.321 0 2.753-.327 4.254-1.194 5.311-3.067 9.632-10.463 9.632-16.556 0-1.979-.463-3.599-1.326-4.761.411 1.035.625 2.275.625 3.635 0 6.243-4.426 13.883-9.865 17.023-1.574.909-3.084 1.317-4.49 1.317-.641 0-1.243-.149-1.799-.341z' fill='#607D8B'/><path d='M113.097 197.23c5.384-3.108 9.748-10.636 9.748-16.814 0-2.051-.483-3.692-1.323-4.86-1.784-1.252-4.374-1.194-7.257.47-5.384 3.108-9.748 10.636-9.748 16.814 0 2.051.483 3.692 1.323 4.86 1.784 1.252 4.374 1.194 7.257-.47' fill='#FAFAFA'/><path d='M108.724 198.614c-1.142 0-2.158-.213-3.019-.817-.021-.014-.04.014-.055-.007-.894-1.244-1.367-2.948-1.367-4.973 0-6.242 4.426-13.864 9.865-17.005 1.574-.908 3.084-1.363 4.49-1.363 1.142 0 2.158.309 3.018.913a.23.23 0 0 1 .056.056c.894 1.244 1.367 2.972 1.367 4.997 0 6.243-4.426 13.783-9.865 16.923-1.574.909-3.084 1.276-4.49 1.276zm-2.718-1.109c.774.532 1.688.776 2.718.776 1.323 0 2.754-.413 4.256-1.28 5.311-3.066 9.631-10.505 9.631-16.598 0-1.909-.434-3.523-1.255-4.685-.774-.533-1.688-.799-2.718-.799-1.323 0-2.755.441-4.256 1.308-5.311 3.066-9.631 10.506-9.631 16.599 0 1.909.434 3.517 1.255 4.679z' fill='#607D8B'/><path d='M149.318 114.262l-9.984 8.878 15.893 11.031 5.589-6.112-11.498-13.797' fill='#FAFAFA'/><path d='M169.676 120.84l-9.748 5.627c-3.642 2.103-9.528 2.113-13.147.024-3.62-2.089-3.601-5.488.041-7.591l9.495-5.608-6.729-3.885-81.836 47.071 45.923 26.514 3.081-1.779c.631-.365.869-.898.618-1.39-2.357-4.632-2.593-9.546-.683-14.262 5.638-13.92 24.509-24.815 48.618-28.07 8.169-1.103 16.68-.967 24.704.394.852.145 1.776.008 2.407-.357l3.081-1.778-25.825-14.91' fill='#FAFAFA'/><path d='M113.675 183.459a.47.47 0 0 1-.233-.062l-45.924-26.515a.468.468 0 0 1 .001-.809l81.836-47.071a.467.467 0 0 1 .466 0l6.729 3.885a.467.467 0 0 1-.467.809l-6.496-3.75-80.9 46.533 44.988 25.973 2.848-1.644c.192-.111.62-.409.435-.773-2.416-4.748-2.658-9.814-.7-14.65 2.806-6.927 8.885-13.242 17.582-18.263 8.657-4.998 19.518-8.489 31.407-10.094 8.198-1.107 16.79-.97 24.844.397.739.125 1.561.007 2.095-.301l2.381-1.374-25.125-14.506a.467.467 0 0 1 .467-.809l25.825 14.91a.467.467 0 0 1 0 .809l-3.081 1.779c-.721.417-1.763.575-2.718.413-7.963-1.351-16.457-1.486-24.563-.392-11.77 1.589-22.512 5.039-31.065 9.977-8.514 4.916-14.456 11.073-17.183 17.805-1.854 4.578-1.623 9.376.666 13.875.37.725.055 1.513-.8 2.006l-3.081 1.78a.476.476 0 0 1-.234.062' fill='#455A64'/><path d='M153.316 128.279c-2.413 0-4.821-.528-6.652-1.586-1.818-1.049-2.82-2.461-2.82-3.975 0-1.527 1.016-2.955 2.861-4.02l9.493-5.607a.233.233 0 1 1 .238.402l-9.496 5.609c-1.696.979-2.628 2.263-2.628 3.616 0 1.34.918 2.608 2.585 3.571 3.549 2.049 9.343 2.038 12.914-.024l9.748-5.628a.234.234 0 0 1 .234.405l-9.748 5.628c-1.858 1.072-4.296 1.609-6.729 1.609' fill='#607D8B'/><path d='M113.675 182.992l-45.913-26.508M113.675 183.342a.346.346 0 0 1-.175-.047l-45.913-26.508a.35.35 0 1 1 .35-.607l45.913 26.508a.35.35 0 0 1-.175.654' fill='#455A64'/><path d='M67.762 156.484v54.001c0 1.09.77 2.418 1.72 2.967l42.473 24.521c.95.549 1.72.11 1.72-.98v-54.001' fill='#FAFAFA'/><path d='M112.727 238.561c-.297 0-.62-.095-.947-.285l-42.473-24.521c-1.063-.613-1.895-2.05-1.895-3.27v-54.001a.35.35 0 1 1 .701 0v54.001c0 .96.707 2.18 1.544 2.663l42.473 24.522c.344.198.661.243.87.122.206-.119.325-.411.325-.799v-54.001a.35.35 0 1 1 .7 0v54.001c0 .655-.239 1.154-.675 1.406a1.235 1.235 0 0 1-.623.162' fill='#455A64'/><path d='M112.86 147.512h-.001c-2.318 0-4.499-.522-6.142-1.471-1.705-.984-2.643-2.315-2.643-3.749 0-1.445.952-2.791 2.68-3.788l12.041-6.953c1.668-.962 3.874-1.493 6.212-1.493 2.318 0 4.499.523 6.143 1.472 1.704.984 2.643 2.315 2.643 3.748 0 1.446-.952 2.791-2.68 3.789l-12.042 6.952c-1.668.963-3.874 1.493-6.211 1.493zm12.147-16.753c-2.217 0-4.298.497-5.861 1.399l-12.042 6.952c-1.502.868-2.33 1.998-2.33 3.182 0 1.173.815 2.289 2.293 3.142 1.538.889 3.596 1.378 5.792 1.378h.001c2.216 0 4.298-.497 5.861-1.399l12.041-6.953c1.502-.867 2.33-1.997 2.33-3.182 0-1.172-.814-2.288-2.292-3.142-1.539-.888-3.596-1.377-5.793-1.377z' fill='#607D8B'/><path d='M165.63 123.219l-5.734 3.311c-3.167 1.828-8.286 1.837-11.433.02-3.147-1.817-3.131-4.772.036-6.601l5.734-3.31 11.397 6.58' fill='#FAFAFA'/><path d='M154.233 117.448l9.995 5.771-4.682 2.704c-1.434.827-3.352 1.283-5.399 1.283-2.029 0-3.923-.449-5.333-1.263-1.29-.744-2-1.694-2-2.674 0-.991.723-1.955 2.036-2.713l5.383-3.108m0-.809l-5.734 3.31c-3.167 1.829-3.183 4.784-.036 6.601 1.568.905 3.623 1.357 5.684 1.357 2.077 0 4.159-.46 5.749-1.377l5.734-3.311-11.397-6.58M145.445 179.667c-1.773 0-3.241-.85-4.243-2.245-.067-.092-.057-.275.023-.356.08-.081.207-.12.3-.055.781.548 1.706.812 2.751.811 1.322 0 2.754-.446 4.256-1.313 5.31-3.066 9.631-10.522 9.631-16.615 0-1.927-.442-3.562-1.279-4.726a.235.235 0 0 1 .024-.301.232.232 0 0 1 .3-.027c1.67 1.172 2.59 3.38 2.59 6.219 0 6.242-4.425 13.987-9.865 17.127-1.573.908-3.083 1.481-4.488 1.481zM142.476 178c.814.651 1.82 1.002 2.969 1.002 1.322 0 2.753-.452 4.255-1.32 5.31-3.065 9.631-10.523 9.631-16.617 0-1.98-.463-3.63-1.325-4.793.411 1.035.624 2.26.624 3.62 0 6.242-4.425 13.875-9.865 17.015-1.573.909-3.084 1.376-4.489 1.376a5.49 5.49 0 0 1-1.8-.283z' fill='#607D8B'/><path d='M148.648 176.704c5.384-3.108 9.748-10.636 9.748-16.813 0-2.052-.483-3.693-1.322-4.861-1.785-1.252-4.375-1.194-7.258.471-5.383 3.108-9.748 10.636-9.748 16.813 0 2.051.484 3.692 1.323 4.86 1.785 1.253 4.374 1.195 7.257-.47' fill='#FAFAFA'/><path d='M144.276 178.276c-1.143 0-2.158-.307-3.019-.911a.217.217 0 0 1-.055-.054c-.895-1.244-1.367-2.972-1.367-4.997 0-6.241 4.425-13.875 9.865-17.016 1.573-.908 3.084-1.369 4.489-1.369 1.143 0 2.158.307 3.019.91a.24.24 0 0 1 .055.055c.894 1.244 1.367 2.971 1.367 4.997 0 6.241-4.425 13.875-9.865 17.016-1.573.908-3.084 1.369-4.489 1.369zm-2.718-1.172c.773.533 1.687.901 2.718.901 1.322 0 2.754-.538 4.256-1.405 5.31-3.066 9.631-10.567 9.631-16.661 0-1.908-.434-3.554-1.256-4.716-.774-.532-1.688-.814-2.718-.814-1.322 0-2.754.433-4.256 1.3-5.31 3.066-9.631 10.564-9.631 16.657 0 1.91.434 3.576 1.256 4.738z' fill='#607D8B'/><path d='M150.72 172.361l-.363-.295a24.105 24.105 0 0 0 2.148-3.128 24.05 24.05 0 0 0 1.977-4.375l.443.149a24.54 24.54 0 0 1-2.015 4.46 24.61 24.61 0 0 1-2.19 3.189M115.917 191.514l-.363-.294a24.174 24.174 0 0 0 2.148-3.128 24.038 24.038 0 0 0 1.976-4.375l.443.148a24.48 24.48 0 0 1-2.015 4.461 24.662 24.662 0 0 1-2.189 3.188M114 237.476V182.584 237.476' fill='#607D8B'/><g><path d='M81.822 37.474c.017-.135-.075-.28-.267-.392-.327-.188-.826-.21-1.109-.045l-6.012 3.471c-.131.076-.194.178-.191.285.002.132.002.461.002.578v.043l-.007.128-6.591 3.779c-.001 0-2.077 1.046-2.787 5.192 0 0-.912 6.961-.898 19.745.015 12.57.606 17.07 1.167 21.351.22 1.684 3.001 2.125 3.001 2.125.331.04.698-.027 1.08-.248l75.273-43.551c1.808-1.069 2.667-3.719 3.056-6.284 1.213-7.99 1.675-32.978-.275-39.878-.196-.693-.51-1.083-.868-1.282l-2.086-.79c-.727.028-1.416.467-1.534.535L82.032 37.072l-.21.402' fill='#FFF'/><path d='M144.311 1.701l2.085.79c.358.199.672.589.868 1.282 1.949 6.9 1.487 31.887.275 39.878-.39 2.565-1.249 5.215-3.056 6.284L69.21 93.486a1.78 1.78 0 0 1-.896.258l-.183-.011c0 .001-2.782-.44-3.003-2.124-.56-4.282-1.151-8.781-1.165-21.351-.015-12.784.897-19.745.897-19.745.71-4.146 2.787-5.192 2.787-5.192l6.591-3.779.007-.128v-.043c0-.117 0-.446-.002-.578-.003-.107.059-.21.191-.285l6.012-3.472a.98.98 0 0 1 .481-.11c.218 0 .449.053.627.156.193.112.285.258.268.392l.211-.402 60.744-34.836c.117-.068.806-.507 1.534-.535m0-.997l-.039.001c-.618.023-1.283.244-1.974.656l-.021.012-60.519 34.706a2.358 2.358 0 0 0-.831-.15c-.365 0-.704.084-.98.244l-6.012 3.471c-.442.255-.699.69-.689 1.166l.001.15-6.08 3.487c-.373.199-2.542 1.531-3.29 5.898l-.006.039c-.009.07-.92 7.173-.906 19.875.014 12.62.603 17.116 1.172 21.465l.002.015c.308 2.355 3.475 2.923 3.836 2.98l.034.004c.101.013.204.019.305.019a2.77 2.77 0 0 0 1.396-.392l75.273-43.552c1.811-1.071 2.999-3.423 3.542-6.997 1.186-7.814 1.734-33.096-.301-40.299-.253-.893-.704-1.527-1.343-1.882l-.132-.062-2.085-.789a.973.973 0 0 0-.353-.065' fill='#455A64'/><path d='M128.267 11.565l1.495.434-56.339 32.326' fill='#FFF'/><path d='M74.202 90.545a.5.5 0 0 1-.25-.931l18.437-10.645a.499.499 0 1 1 .499.864L74.451 90.478l-.249.067M75.764 42.654l-.108-.062.046-.171 5.135-2.964.17.045-.045.171-5.135 2.964-.063.017M70.52 90.375V46.421l.063-.036L137.84 7.554v43.954l-.062.036L70.52 90.375zm.25-43.811v43.38l66.821-38.579V7.985L70.77 46.564z' fill='#607D8B'/><path d='M86.986 83.182c-.23.149-.612.384-.849.523l-11.505 6.701c-.237.139-.206.252.068.252h.565c.275 0 .693-.113.93-.252L87.7 83.705c.237-.139.428-.253.425-.256a11.29 11.29 0 0 1-.006-.503c0-.274-.188-.377-.418-.227l-.715.463' fill='#607D8B'/><path d='M75.266 90.782H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.236-.138.615-.371.844-.519l.715-.464a.488.488 0 0 1 .266-.089c.172 0 .345.13.345.421 0 .214.001.363.003.437l.006.004-.004.069c-.003.075-.003.075-.486.356l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.108.866-.234l11.505-6.702c.168-.098.294-.173.361-.214-.004-.084-.004-.218-.004-.437l-.095-.171-.131.049-.714.463c-.232.15-.616.386-.854.525l-11.505 6.702-.029.018z' fill='#607D8B'/><path d='M75.266 89.871H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.258-.151.694-.268.993-.268h.565c.2 0 .316.056.346.166.03.11-.043.217-.215.317l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.107.866-.234l11.505-6.702.03-.018-.035-.001h-.565c-.252 0-.649.108-.867.234l-11.505 6.702-.029.018zM74.37 90.801v-1.247 1.247' fill='#607D8B'/><path d='M68.13 93.901c-.751-.093-1.314-.737-1.439-1.376-.831-4.238-1.151-8.782-1.165-21.352-.015-12.784.897-19.745.897-19.745.711-4.146 2.787-5.192 2.787-5.192l74.859-43.219c.223-.129 2.487-1.584 3.195.923 1.95 6.9 1.488 31.887.275 39.878-.389 2.565-1.248 5.215-3.056 6.283L69.21 93.653c-.382.221-.749.288-1.08.248 0 0-2.781-.441-3.001-2.125-.561-4.281-1.152-8.781-1.167-21.351-.014-12.784.898-19.745.898-19.745.71-4.146 2.787-5.191 2.787-5.191l6.598-3.81.871-.119 6.599-3.83.046-.461L68.13 93.901' fill='#FAFAFA'/><path d='M68.317 94.161l-.215-.013h-.001l-.244-.047c-.719-.156-2.772-.736-2.976-2.292-.568-4.34-1.154-8.813-1.168-21.384-.014-12.654.891-19.707.9-19.777.725-4.231 2.832-5.338 2.922-5.382l6.628-3.827.87-.119 6.446-3.742.034-.334a.248.248 0 0 1 .273-.223.248.248 0 0 1 .223.272l-.059.589-6.752 3.919-.87.118-6.556 3.785c-.031.016-1.99 1.068-2.666 5.018-.007.06-.908 7.086-.894 19.702.014 12.539.597 16.996 1.161 21.305.091.691.689 1.154 1.309 1.452a1.95 1.95 0 0 1-.236-.609c-.781-3.984-1.155-8.202-1.17-21.399-.014-12.653.891-19.707.9-19.777.725-4.231 2.832-5.337 2.922-5.382-.004.001 74.444-42.98 74.846-43.212l.028-.017c.904-.538 1.72-.688 2.36-.433.555.221.949.733 1.172 1.52 2.014 7.128 1.46 32.219.281 39.983-.507 3.341-1.575 5.515-3.175 6.462L69.335 93.869a2.023 2.023 0 0 1-1.018.292zm-.147-.507c.293.036.604-.037.915-.217l75.273-43.551c1.823-1.078 2.602-3.915 2.934-6.106 1.174-7.731 1.731-32.695-.268-39.772-.178-.631-.473-1.032-.876-1.192-.484-.193-1.166-.052-1.921.397l-.034.021-74.858 43.218c-.031.017-1.989 1.069-2.666 5.019-.007.059-.908 7.085-.894 19.702.015 13.155.386 17.351 1.161 21.303.09.461.476.983 1.037 1.139.114.025.185.037.196.039h.001z' fill='#455A64'/><path d='M69.317 68.982c.489-.281.885-.056.885.505 0 .56-.396 1.243-.885 1.525-.488.282-.884.057-.884-.504 0-.56.396-1.243.884-1.526' fill='#FFF'/><path d='M68.92 71.133c-.289 0-.487-.228-.487-.625 0-.56.396-1.243.884-1.526a.812.812 0 0 1 .397-.121c.289 0 .488.229.488.626 0 .56-.396 1.243-.885 1.525a.812.812 0 0 1-.397.121m.794-2.459a.976.976 0 0 0-.49.147c-.548.317-.978 1.058-.978 1.687 0 .486.271.812.674.812a.985.985 0 0 0 .491-.146c.548-.317.978-1.057.978-1.687 0-.486-.272-.813-.675-.813' fill='#8097A2'/><path d='M68.92 70.947c-.271 0-.299-.307-.299-.439 0-.491.361-1.116.79-1.363a.632.632 0 0 1 .303-.096c.272 0 .301.306.301.438 0 .491-.363 1.116-.791 1.364a.629.629 0 0 1-.304.096m.794-2.086a.812.812 0 0 0-.397.121c-.488.283-.884.966-.884 1.526 0 .397.198.625.487.625a.812.812 0 0 0 .397-.121c.489-.282.885-.965.885-1.525 0-.397-.199-.626-.488-.626' fill='#8097A2'/><path d='M69.444 85.35c.264-.152.477-.031.477.272 0 .303-.213.67-.477.822-.263.153-.477.031-.477-.271 0-.302.214-.671.477-.823' fill='#FFF'/><path d='M69.23 86.51c-.156 0-.263-.123-.263-.337 0-.302.214-.671.477-.823a.431.431 0 0 1 .214-.066c.156 0 .263.124.263.338 0 .303-.213.67-.477.822a.431.431 0 0 1-.214.066m.428-1.412c-.1 0-.203.029-.307.09-.32.185-.57.618-.57.985 0 .309.185.524.449.524a.63.63 0 0 0 .308-.09c.32-.185.57-.618.57-.985 0-.309-.185-.524-.45-.524' fill='#8097A2'/><path d='M69.23 86.322l-.076-.149c0-.235.179-.544.384-.661l.12-.041.076.151c0 .234-.179.542-.383.66l-.121.04m.428-1.038a.431.431 0 0 0-.214.066c-.263.152-.477.521-.477.823 0 .214.107.337.263.337a.431.431 0 0 0 .214-.066c.264-.152.477-.519.477-.822 0-.214-.107-.338-.263-.338' fill='#8097A2'/><path d='M139.278 7.769v43.667L72.208 90.16V46.493l67.07-38.724' fill='#455A64'/><path d='M72.083 90.375V46.421l.063-.036 67.257-38.831v43.954l-.062.036-67.258 38.831zm.25-43.811v43.38l66.821-38.579V7.985L72.333 46.564z' fill='#607D8B'/></g><path d='M125.737 88.647l-7.639 3.334V84l-11.459 4.713v8.269L99 100.315l13.369 3.646 13.368-15.314' fill='#455A64'/></g></svg>")};var ae="CardboardV1",oe="WEBVR_CARDBOARD_VIEWER";function le(e){try{this.selectedKey=localStorage.getItem(oe)}catch(e){console.error("Failed to load viewer profile: %s",e)}this.selectedKey||(this.selectedKey=e||ae),this.dialog=this.createDialog_(W.Viewers),this.root=null,this.onChangeCallbacks_=[]}le.prototype.show=function(e){this.root=e,e.appendChild(this.dialog);var t=this.dialog.querySelector("#"+this.selectedKey);t.checked=!0,this.dialog.style.display="block"},le.prototype.hide=function(){this.root&&this.root.contains(this.dialog)&&this.root.removeChild(this.dialog),this.dialog.style.display="none"},le.prototype.getCurrentViewer=function(){return W.Viewers[this.selectedKey]},le.prototype.getSelectedKey_=function(){var e=this.dialog.querySelector("input[name=field]:checked");return e?e.id:null},le.prototype.onChange=function(e){this.onChangeCallbacks_.push(e)},le.prototype.fireOnChange_=function(e){for(var t=0;t<this.onChangeCallbacks_.length;t++)this.onChangeCallbacks_[t](e)},le.prototype.onSave_=function(){if(this.selectedKey=this.getSelectedKey_(),this.selectedKey&&W.Viewers[this.selectedKey]){this.fireOnChange_(W.Viewers[this.selectedKey]);try{localStorage.setItem(oe,this.selectedKey)}catch(e){console.error("Failed to save viewer profile: %s",e)}this.hide()}else console.error("ViewerSelector.onSave_: this should never happen!")},le.prototype.createDialog_=function(e){var t=document.createElement("div");t.classList.add("webvr-polyfill-viewer-selector"),t.style.display="none";var i=document.createElement("div"),r=i.style;r.position="fixed",r.left=0,r.top=0,r.width="100%",r.height="100%",r.background="rgba(0, 0, 0, 0.3)",i.addEventListener("click",this.hide.bind(this));var n=document.createElement("div"),r=n.style;for(var s in r.boxSizing="border-box",r.position="fixed",r.top="24px",r.left="50%",r.marginLeft="-140px",r.width="280px",r.padding="24px",r.overflow="hidden",r.background="#fafafa",r.fontFamily="'Roboto', sans-serif",r.boxShadow="0px 5px 20px #666",n.appendChild(this.createH1_("Select your viewer")),e)n.appendChild(this.createChoice_(s,e[s].label));return n.appendChild(this.createButton_("Save",this.onSave_.bind(this))),t.appendChild(i),t.appendChild(n),t},le.prototype.createH1_=function(e){var t=document.createElement("h1"),i=t.style;return i.color="black",i.fontSize="20px",i.fontWeight="bold",i.marginTop=0,i.marginBottom="24px",t.innerHTML=e,t},le.prototype.createChoice_=function(e,t){var i=document.createElement("div");i.style.marginTop="8px",i.style.color="black";var r=document.createElement("input");r.style.fontSize="30px",r.setAttribute("id",e),r.setAttribute("type","radio"),r.setAttribute("value",e),r.setAttribute("name","field");var n=document.createElement("label");return n.style.marginLeft="4px",n.setAttribute("for",e),n.innerHTML=t,i.appendChild(r),i.appendChild(n),i},le.prototype.createButton_=function(e,t){var i=document.createElement("button");i.innerHTML=e;var r=i.style;return r.float="right",r.textTransform="uppercase",r.color="#1094f7",r.fontSize="14px",r.letterSpacing=0,r.border=0,r.background="none",r.marginTop="16px",i.addEventListener("click",t),i},"undefined"!=typeof window?window:void 0!==Ve||"undefined"!=typeof self&&self;var Ae,he,ce=(function(e,t){var i;i=function(){return function(e){var t={};function i(r){if(t[r])return t[r].exports;var n=t[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,i),n.l=!0,n.exports}return i.m=e,i.c=t,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=0)}([function(e,t,i){var r=function(){function e(e,t){for(var i=0;i<t.length;i++){var r=t[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,i,r){return i&&e(t.prototype,i),r&&e(t,r),t}}(),n=i(1),s="undefined"!=typeof navigator&&parseFloat((""+(/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))<10&&!window.MSStream,a=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),s?this.noSleepTimer=null:(this.noSleepVideo=document.createElement("video"),this.noSleepVideo.setAttribute("playsinline",""),this.noSleepVideo.setAttribute("src",n),this.noSleepVideo.addEventListener("timeupdate",function(e){this.noSleepVideo.currentTime>.5&&(this.noSleepVideo.currentTime=Math.random())}.bind(this)))}return r(e,[{key:"enable",value:function(){s?(this.disable(),this.noSleepTimer=window.setInterval(function(){window.location.href="/",window.setTimeout(window.stop,0)},15e3)):this.noSleepVideo.play()}},{key:"disable",value:function(){s?this.noSleepTimer&&(window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):this.noSleepVideo.pause()}}]),e}();e.exports=a},function(e,t,i){e.exports="data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA="}])},e.exports=i()}(he={exports:{}},he.exports),(Ae=he.exports)&&Ae.__esModule&&Object.prototype.hasOwnProperty.call(Ae,"default")?Ae.default:Ae),de=1e3,ue=[0,0,.5,1],pe=[.5,0,.5,1],fe=window.requestAnimationFrame,me=window.cancelAnimationFrame;function ge(e){Object.defineProperties(this,{hasPosition:{writable:!1,enumerable:!0,value:e.hasPosition},hasExternalDisplay:{writable:!1,enumerable:!0,value:e.hasExternalDisplay},canPresent:{writable:!1,enumerable:!0,value:e.canPresent},maxLayers:{writable:!1,enumerable:!0,value:e.maxLayers},hasOrientation:{enumerable:!0,get:function(){return x("VRDisplayCapabilities.prototype.hasOrientation","VRDisplay.prototype.getFrameData"),e.hasOrientation}}})}function we(e){var t=!("wakelock"in(e=e||{}))||e.wakelock;this.isPolyfilled=!0,this.displayId=de++,this.displayName="",this.depthNear=.01,this.depthFar=1e4,this.isPresenting=!1,Object.defineProperty(this,"isConnected",{get:function(){return x("VRDisplay.prototype.isConnected","VRDisplayCapabilities.prototype.hasExternalDisplay"),!1}}),this.capabilities=new ge({hasPosition:!1,hasOrientation:!1,hasExternalDisplay:!1,canPresent:!1,maxLayers:1}),this.stageParameters=null,this.waitingForPresent_=!1,this.layer_=null,this.originalParent_=null,this.fullscreenElement_=null,this.fullscreenWrapper_=null,this.fullscreenElementCachedStyle_=null,this.fullscreenEventTarget_=null,this.fullscreenChangeHandler_=null,this.fullscreenErrorHandler_=null,t&&v()&&(this.wakelock_=new ce)}we.prototype.getFrameData=function(e){return E(e,this._getPose(),this)},we.prototype.getPose=function(){return x("VRDisplay.prototype.getPose","VRDisplay.prototype.getFrameData"),this._getPose()},we.prototype.resetPose=function(){return x("VRDisplay.prototype.resetPose"),this._resetPose()},we.prototype.getImmediatePose=function(){return x("VRDisplay.prototype.getImmediatePose","VRDisplay.prototype.getFrameData"),this._getPose()},we.prototype.requestAnimationFrame=function(e){return fe(e)},we.prototype.cancelAnimationFrame=function(e){return me(e)},we.prototype.wrapForFullscreen=function(e){if(o())return e;if(!this.fullscreenWrapper_){this.fullscreenWrapper_=document.createElement("div");var t=["height: "+Math.min(screen.height,screen.width)+"px !important","top: 0 !important","left: 0 !important","right: 0 !important","border: 0","margin: 0","padding: 0","z-index: 999999 !important","position: fixed"];this.fullscreenWrapper_.setAttribute("style",t.join("; ")+";"),this.fullscreenWrapper_.classList.add("webvr-polyfill-fullscreen-wrapper")}if(this.fullscreenElement_==e)return this.fullscreenWrapper_;if(this.fullscreenElement_&&(this.originalParent_?this.originalParent_.appendChild(this.fullscreenElement_):this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_)),this.fullscreenElement_=e,this.originalParent_=e.parentElement,this.originalParent_||document.body.appendChild(e),!this.fullscreenWrapper_.parentElement){var i=this.fullscreenElement_.parentElement;i.insertBefore(this.fullscreenWrapper_,this.fullscreenElement_),i.removeChild(this.fullscreenElement_)}this.fullscreenWrapper_.insertBefore(this.fullscreenElement_,this.fullscreenWrapper_.firstChild),this.fullscreenElementCachedStyle_=this.fullscreenElement_.getAttribute("style");var r=this;return function(){if(r.fullscreenElement_){var e=["position: absolute","top: 0","left: 0","width: "+Math.max(screen.width,screen.height)+"px","height: "+Math.min(screen.height,screen.width)+"px","border: 0","margin: 0","padding: 0"];r.fullscreenElement_.setAttribute("style",e.join("; ")+";")}}(),this.fullscreenWrapper_},we.prototype.removeFullscreenWrapper=function(){if(this.fullscreenElement_){var e=this.fullscreenElement_;this.fullscreenElementCachedStyle_?e.setAttribute("style",this.fullscreenElementCachedStyle_):e.removeAttribute("style"),this.fullscreenElement_=null,this.fullscreenElementCachedStyle_=null;var t=this.fullscreenWrapper_.parentElement;return this.fullscreenWrapper_.removeChild(e),this.originalParent_===t?t.insertBefore(e,this.fullscreenWrapper_):this.originalParent_&&this.originalParent_.appendChild(e),t.removeChild(this.fullscreenWrapper_),e}},we.prototype.requestPresent=function(e){var t=this.isPresenting,i=this;return e instanceof Array||(x("VRDisplay.prototype.requestPresent with non-array argument","an array of VRLayers as the first argument"),e=[e]),new Promise(function(r,n){if(i.capabilities.canPresent)if(0==e.length||e.length>i.capabilities.maxLayers)n(new Error("Invalid number of layers."));else{var s=e[0];if(s.source){var a=s.leftBounds||ue,A=s.rightBounds||pe;if(t){var h=i.layer_;h.source!==s.source&&(h.source=s.source);for(var c=0;c<4;c++)h.leftBounds[c]=a[c],h.rightBounds[c]=A[c];return i.wrapForFullscreen(i.layer_.source),i.updatePresent_(),void r()}if(i.layer_={predistorted:s.predistorted,source:s.source,leftBounds:a.slice(0),rightBounds:A.slice(0)},i.waitingForPresent_=!1,i.layer_&&i.layer_.source){var d=i.wrapForFullscreen(i.layer_.source);i.addFullscreenListeners_(d,function(){var e=document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement;i.isPresenting=d===e,i.isPresenting?(screen.orientation&&screen.orientation.lock&&screen.orientation.lock("landscape-primary").catch(function(e){console.error("screen.orientation.lock() failed due to",e.message)}),i.waitingForPresent_=!1,i.beginPresent_(),r()):(screen.orientation&&screen.orientation.unlock&&screen.orientation.unlock(),i.removeFullscreenWrapper(),i.disableWakeLock(),i.endPresent_(),i.removeFullscreenListeners_()),i.fireVRDisplayPresentChange_()},function(){i.waitingForPresent_&&(i.removeFullscreenWrapper(),i.removeFullscreenListeners_(),i.disableWakeLock(),i.waitingForPresent_=!1,i.isPresenting=!1,n(new Error("Unable to present.")))}),function(e){if(l())return!1;if(e.requestFullscreen)e.requestFullscreen();else if(e.webkitRequestFullscreen)e.webkitRequestFullscreen();else if(e.mozRequestFullScreen)e.mozRequestFullScreen();else{if(!e.msRequestFullscreen)return!1;e.msRequestFullscreen()}return!0}(d)?(i.enableWakeLock(),i.waitingForPresent_=!0):(o()||l())&&(i.enableWakeLock(),i.isPresenting=!0,i.beginPresent_(),i.fireVRDisplayPresentChange_(),r())}i.waitingForPresent_||o()||(m(),n(new Error("Unable to present.")))}else r()}else n(new Error("VRDisplay is not capable of presenting."))})},we.prototype.exitPresent=function(){var e=this.isPresenting,t=this;return this.isPresenting=!1,this.layer_=null,this.disableWakeLock(),new Promise(function(i,r){e?(!m()&&o()&&(t.endPresent_(),t.fireVRDisplayPresentChange_()),l()&&(t.removeFullscreenWrapper(),t.removeFullscreenListeners_(),t.endPresent_(),t.fireVRDisplayPresentChange_()),i()):r(new Error("Was not presenting to VRDisplay."))})},we.prototype.getLayers=function(){return this.layer_?[this.layer_]:[]},we.prototype.fireVRDisplayPresentChange_=function(){var e=new CustomEvent("vrdisplaypresentchange",{detail:{display:this}});window.dispatchEvent(e)},we.prototype.fireVRDisplayConnect_=function(){var e=new CustomEvent("vrdisplayconnect",{detail:{display:this}});window.dispatchEvent(e)},we.prototype.addFullscreenListeners_=function(e,t,i){this.removeFullscreenListeners_(),this.fullscreenEventTarget_=e,this.fullscreenChangeHandler_=t,this.fullscreenErrorHandler_=i,t&&(document.fullscreenEnabled?e.addEventListener("fullscreenchange",t,!1):document.webkitFullscreenEnabled?e.addEventListener("webkitfullscreenchange",t,!1):document.mozFullScreenEnabled?document.addEventListener("mozfullscreenchange",t,!1):document.msFullscreenEnabled&&e.addEventListener("msfullscreenchange",t,!1)),i&&(document.fullscreenEnabled?e.addEventListener("fullscreenerror",i,!1):document.webkitFullscreenEnabled?e.addEventListener("webkitfullscreenerror",i,!1):document.mozFullScreenEnabled?document.addEventListener("mozfullscreenerror",i,!1):document.msFullscreenEnabled&&e.addEventListener("msfullscreenerror",i,!1))},we.prototype.removeFullscreenListeners_=function(){if(this.fullscreenEventTarget_){var e=this.fullscreenEventTarget_;if(this.fullscreenChangeHandler_){var t=this.fullscreenChangeHandler_;e.removeEventListener("fullscreenchange",t,!1),e.removeEventListener("webkitfullscreenchange",t,!1),document.removeEventListener("mozfullscreenchange",t,!1),e.removeEventListener("msfullscreenchange",t,!1)}if(this.fullscreenErrorHandler_){var i=this.fullscreenErrorHandler_;e.removeEventListener("fullscreenerror",i,!1),e.removeEventListener("webkitfullscreenerror",i,!1),document.removeEventListener("mozfullscreenerror",i,!1),e.removeEventListener("msfullscreenerror",i,!1)}this.fullscreenEventTarget_=null,this.fullscreenChangeHandler_=null,this.fullscreenErrorHandler_=null}},we.prototype.enableWakeLock=function(){this.wakelock_&&this.wakelock_.enable()},we.prototype.disableWakeLock=function(){this.wakelock_&&this.wakelock_.disable()},we.prototype.beginPresent_=function(){},we.prototype.endPresent_=function(){},we.prototype.submitFrame=function(e){},we.prototype.getEyeParameters=function(e){return null};var ve={ADDITIONAL_VIEWERS:[],DEFAULT_VIEWER:"",MOBILE_WAKE_LOCK:!0,DEBUG:!1,DPDB_URL:"https://dpdb.webvr.rocks/dpdb.json",K_FILTER:.98,PREDICTION_TIME_S:.04,CARDBOARD_UI_DISABLED:!1,ROTATE_INSTRUCTIONS_DISABLED:!1,YAW_ONLY:!1,BUFFER_SCALE:.5,DIRTY_SUBMIT_FRAME_BINDINGS:!1},ye={LEFT:"left",RIGHT:"right"};function be(e){var t=y({},ve);e=y(t,e||{}),we.call(this,{wakelock:e.MOBILE_WAKE_LOCK}),this.config=e,this.displayName="Cardboard VRDisplay",this.capabilities=new ge({hasPosition:!1,hasOrientation:!0,hasExternalDisplay:!1,canPresent:!0,maxLayers:1}),this.stageParameters=null,this.bufferScale_=this.config.BUFFER_SCALE,this.poseSensor_=new ne(this.config),this.distorter_=null,this.cardboardUI_=null,this.dpdb_=new Z(this.config.DPDB_URL,this.onDeviceParamsUpdated_.bind(this)),this.deviceInfo_=new W(this.dpdb_.getDeviceParams(),e.ADDITIONAL_VIEWERS),this.viewerSelector_=new le(e.DEFAULT_VIEWER),this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)),this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()),this.config.ROTATE_INSTRUCTIONS_DISABLED||(this.rotateInstructions_=new se),o()&&window.addEventListener("resize",this.onResize_.bind(this))}return be.prototype=Object.create(we.prototype),be.prototype._getPose=function(){return{position:null,orientation:this.poseSensor_.getOrientation(),linearVelocity:null,linearAcceleration:null,angularVelocity:null,angularAcceleration:null}},be.prototype._resetPose=function(){this.poseSensor_.resetPose&&this.poseSensor_.resetPose()},be.prototype._getFieldOfView=function(e){var t;if(e==ye.LEFT)t=this.deviceInfo_.getFieldOfViewLeftEye();else{if(e!=ye.RIGHT)return console.error("Invalid eye provided: %s",e),null;t=this.deviceInfo_.getFieldOfViewRightEye()}return t},be.prototype._getEyeOffset=function(e){var t;if(e==ye.LEFT)t=[.5*-this.deviceInfo_.viewer.interLensDistance,0,0];else{if(e!=ye.RIGHT)return console.error("Invalid eye provided: %s",e),null;t=[.5*this.deviceInfo_.viewer.interLensDistance,0,0]}return t},be.prototype.getEyeParameters=function(e){var t=this._getEyeOffset(e),i=this._getFieldOfView(e),r={offset:t,renderWidth:.5*this.deviceInfo_.device.width*this.bufferScale_,renderHeight:this.deviceInfo_.device.height*this.bufferScale_};return Object.defineProperty(r,"fieldOfView",{enumerable:!0,get:function(){return x("VRFieldOfView","VRFrameData's projection matrices"),i}}),r},be.prototype.onDeviceParamsUpdated_=function(e){this.config.DEBUG&&console.log("DPDB reported that device params were updated."),this.deviceInfo_.updateDeviceParams(e),this.distorter_&&this.distorter_.updateDeviceInfo(this.deviceInfo_)},be.prototype.updateBounds_=function(){this.layer_&&this.distorter_&&(this.layer_.leftBounds||this.layer_.rightBounds)&&this.distorter_.setTextureBounds(this.layer_.leftBounds,this.layer_.rightBounds)},be.prototype.beginPresent_=function(){var e=this.layer_.source.getContext("webgl");e||(e=this.layer_.source.getContext("experimental-webgl")),e||(e=this.layer_.source.getContext("webgl2")),e&&(this.layer_.predistorted?this.config.CARDBOARD_UI_DISABLED||(e.canvas.width=p()*this.bufferScale_,e.canvas.height=f()*this.bufferScale_,this.cardboardUI_=new I(e)):(this.config.CARDBOARD_UI_DISABLED||(this.cardboardUI_=new I(e)),this.distorter_=new T(e,this.cardboardUI_,this.config.BUFFER_SCALE,this.config.DIRTY_SUBMIT_FRAME_BINDINGS),this.distorter_.updateDeviceInfo(this.deviceInfo_)),this.cardboardUI_&&this.cardboardUI_.listen(function(e){this.viewerSelector_.show(this.layer_.source.parentElement),e.stopPropagation(),e.preventDefault()}.bind(this),function(e){this.exitPresent(),e.stopPropagation(),e.preventDefault()}.bind(this)),this.rotateInstructions_&&(u()&&v()?this.rotateInstructions_.showTemporarily(3e3,this.layer_.source.parentElement):this.rotateInstructions_.update()),this.orientationHandler=this.onOrientationChange_.bind(this),window.addEventListener("orientationchange",this.orientationHandler),this.vrdisplaypresentchangeHandler=this.updateBounds_.bind(this),window.addEventListener("vrdisplaypresentchange",this.vrdisplaypresentchangeHandler),this.fireVRDisplayDeviceParamsChange_())},be.prototype.endPresent_=function(){this.distorter_&&(this.distorter_.destroy(),this.distorter_=null),this.cardboardUI_&&(this.cardboardUI_.destroy(),this.cardboardUI_=null),this.rotateInstructions_&&this.rotateInstructions_.hide(),this.viewerSelector_.hide(),window.removeEventListener("orientationchange",this.orientationHandler),window.removeEventListener("vrdisplaypresentchange",this.vrdisplaypresentchangeHandler)},be.prototype.updatePresent_=function(){this.endPresent_(),this.beginPresent_()},be.prototype.submitFrame=function(e){if(this.distorter_)this.updateBounds_(),this.distorter_.submitFrame();else if(this.cardboardUI_&&this.layer_){var t=this.layer_.source.getContext("webgl").canvas;t.width==this.lastWidth&&t.height==this.lastHeight||this.cardboardUI_.onResize(),this.lastWidth=t.width,this.lastHeight=t.height,this.cardboardUI_.render()}},be.prototype.onOrientationChange_=function(e){this.viewerSelector_.hide(),this.rotateInstructions_&&this.rotateInstructions_.update(),this.onResize_()},be.prototype.onResize_=function(e){if(this.layer_){var t=this.layer_.source.getContext("webgl");t.canvas.setAttribute("style",["position: absolute","top: 0","left: 0","width: 100vw","height: 100vh","border: 0","margin: 0","padding: 0px","box-sizing: content-box"].join("; ")+";"),b(t.canvas)}},be.prototype.onViewerChanged_=function(e){this.deviceInfo_.setViewer(e),this.distorter_&&this.distorter_.updateDeviceInfo(this.deviceInfo_),this.fireVRDisplayDeviceParamsChange_()},be.prototype.fireVRDisplayDeviceParamsChange_=function(){var e=new CustomEvent("vrdisplaydeviceparamschange",{detail:{vrdisplay:this,deviceInfo:this.deviceInfo_}});window.dispatchEvent(e)},be.VRFrameData=function(){this.leftProjectionMatrix=new Float32Array(16),this.leftViewMatrix=new Float32Array(16),this.rightProjectionMatrix=new Float32Array(16),this.rightViewMatrix=new Float32Array(16),this.pose=null},be.VRDisplay=we,be}()}(ze={exports:{}},ze.exports),ze.exports),Xe=(Ue=He)&&Ue.__esModule&&Object.prototype.hasOwnProperty.call(Ue,"default")?Ue.default:Ue;class We extends i{constructor(e){super(),this.global=e,this.onWindowResize=this.onWindowResize.bind(this),this.global.window.addEventListener("resize",this.onWindowResize),this.environmentBlendMode="opaque"}get depthNear(){throw new Error("Not implemented")}set depthNear(e){throw new Error("Not implemented")}get depthFar(){throw new Error("Not implemented")}set depthFar(e){throw new Error("Not implemented")}onBaseLayerSet(e,t){throw new Error("Not implemented")}onInlineVerticalFieldOfViewSet(e,t){throw new Error("Not implemented")}supportsSession(e){throw new Error("Not implemented")}async requestSession(e){throw new Error("Not implemented")}requestAnimationFrame(e){throw new Error("Not implemented")}onFrameStart(e){throw new Error("Not implemented")}onFrameEnd(e){throw new Error("Not implemented")}requestStageBounds(){throw new Error("Not implemented")}async requestFrameOfReferenceTransform(e,t){}cancelAnimationFrame(e){throw new Error("Not implemented")}endSession(e){throw new Error("Not implemented")}getViewport(e,t,i,r){throw new Error("Not implemented")}getProjectionMatrix(e){throw new Error("Not implemented")}getBasePoseMatrix(){throw new Error("Not implemented")}getBaseViewMatrix(e){throw new Error("Not implemented")}getInputSources(){throw new Error("Not implemented")}getInputPose(e,t,i){throw new Error("Not implemented")}onWindowResize(){this.onWindowResize()}}let je={mapping:"xr-standard",id:"oculus-touch",buttons:{length:6,0:1,1:0,2:2,3:null,4:3,5:4},gripTransform:{position:[0,-.02,.04,1],orientation:[.11*Math.PI,0,0,1]}},Ye={mapping:"xr-standard",id:"windows-mixed-reality",buttons:{length:4,0:1,1:0,2:2,3:4},gripTransform:{position:[0,-.02,.04,1],orientation:[.11*Math.PI,0,0,1]}},Ze={"Oculus Touch (Right)":je,"Oculus Touch (Left)":je,"Oculus Go Controller":{mapping:"xr-standard",id:"oculus-go",buttons:{0:1,1:0},gripTransform:{orientation:[.11*Math.PI,0,0,1]}},"Windows Mixed Reality (Right)":Ye,"Windows Mixed Reality (Left)":Ye};const qe=E(.155,-.465,-.15),Je=E(-.155,-.465,-.15),Ke=E(0,0,-.25),$e=E(0,0,.05),et=E(-.08,.14,.08),tt=.4,it=.4,rt=.61,nt=.175,st=.12,at=.87,ot=180/Math.PI;class lt{constructor(){this.hand="right",this.headElbowOffset=qe,this.controllerQ=C(),this.lastControllerQ=C(),this.headQ=C(),this.headPos=y(),this.elbowPos=y(),this.wristPos=y(),this.time=null,this.lastTime=null,this.rootQ=C(),this.position=y()}setHandedness(e){this.hand!=e&&(this.hand=e,"left"==this.hand?this.headElbowOffset=Je:this.headElbowOffset=qe)}update(e,t){this.time=o(),e&&(Q(this.lastControllerQ,this.controllerQ),Q(this.controllerQ,e)),t&&(w(this.headPos,t),v(this.headQ,t));let i=this.getHeadYawOrientation_(),r=this.quatAngle_(this.lastControllerQ,this.controllerQ);r/((this.time-this.lastTime)/1e3)>rt?D(this.rootQ,this.rootQ,i,Math.min(r/nt,1)):Q(this.rootQ,i);let n=E(0,0,-1);T(n,n,this.controllerQ);let s=S(n,[0,1,0]),a=this.clamp_((s-st)/at,0,1),l=L(this.rootQ);I(l,l),P(l,l,this.controllerQ);let A=this.elbowPos;M(A,this.headPos),_(A,A,this.headElbowOffset);let h=b(et);x(h,h,a),_(A,A,h);let c=this.quatAngle_(l,C())*ot,d=(1-Math.pow(c/180,4))*(tt+(1-tt)*a*it),u=C();D(u,u,l,d);let p=I(C(),u),f=L(l);P(f,f,p);let m=this.wristPos;M(m,$e),T(m,m,u),_(m,m,Ke),T(m,m,f),_(m,m,A);let g=b(et);x(g,g,a),_(this.position,this.wristPos,g),T(this.position,this.position,this.rootQ),this.lastTime=this.time}getPosition(){return this.position}getHeadYawOrientation_(){let e=y();return function(e,t,i){function r(e,t,i){return e<t?t:e>i?i:e}var n=t[0]*t[0],s=t[1]*t[1],a=t[2]*t[2],o=t[3]*t[3];if("XYZ"===i)e[0]=Math.atan2(2*(t[0]*t[3]-t[1]*t[2]),o-n-s+a),e[1]=Math.asin(r(2*(t[0]*t[2]+t[1]*t[3]),-1,1)),e[2]=Math.atan2(2*(t[2]*t[3]-t[0]*t[1]),o+n-s-a);else if("YXZ"===i)e[0]=Math.asin(r(2*(t[0]*t[3]-t[1]*t[2]),-1,1)),e[1]=Math.atan2(2*(t[0]*t[2]+t[1]*t[3]),o-n-s+a),e[2]=Math.atan2(2*(t[0]*t[1]+t[2]*t[3]),o-n+s-a);else if("ZXY"===i)e[0]=Math.asin(r(2*(t[0]*t[3]+t[1]*t[2]),-1,1)),e[1]=Math.atan2(2*(t[1]*t[3]-t[2]*t[0]),o-n-s+a),e[2]=Math.atan2(2*(t[2]*t[3]-t[0]*t[1]),o-n+s-a);else if("ZYX"===i)e[0]=Math.atan2(2*(t[0]*t[3]+t[2]*t[1]),o-n-s+a),e[1]=Math.asin(r(2*(t[1]*t[3]-t[0]*t[2]),-1,1)),e[2]=Math.atan2(2*(t[0]*t[1]+t[2]*t[3]),o+n-s-a);else if("YZX"===i)e[0]=Math.atan2(2*(t[0]*t[3]-t[2]*t[1]),o-n+s-a),e[1]=Math.atan2(2*(t[1]*t[3]-t[0]*t[2]),o+n-s-a),e[2]=Math.asin(r(2*(t[0]*t[1]+t[2]*t[3]),-1,1));else{if("XZY"!==i)return void console.log("No order given for quaternion to euler conversion.");e[0]=Math.atan2(2*(t[0]*t[3]+t[1]*t[2]),o-n+s-a),e[1]=Math.atan2(2*(t[0]*t[2]+t[1]*t[3]),o+n-s-a),e[2]=Math.asin(r(2*(t[2]*t[3]-t[0]*t[1]),-1,1))}}(e,this.headQ,"YXZ"),function(e,t,i,r){let n=.5*Math.PI/180;t*=n,i*=n,r*=n;let s=Math.sin(t),a=Math.cos(t),o=Math.sin(i),l=Math.cos(i),A=Math.sin(r),h=Math.cos(r);return e[0]=s*l*h-a*o*A,e[1]=a*o*h+s*l*A,e[2]=a*l*A-s*o*h,e[3]=a*l*h+s*o*A,e}(C(),0,e[1]*ot,0)}clamp_(e,t,i){return Math.min(Math.max(e,t),i)}quatAngle_(e,t){let i=[0,0,-1],r=[0,0,-1];return T(i,i,e),T(r,r,t),function(e,t){let i=E(e[0],e[1],e[2]),r=E(t[0],t[1],t[2]);F(i,i),F(r,r);let n=S(i,r);return n>1?0:n<-1?Math.PI:Math.acos(n)}(i,r)}}const At=Symbol("@@webxr-polyfill/XRRemappedGamepad"),ht={pressed:!1,touched:!1,value:0};Object.freeze(ht);class ct{constructor(e,t){t||(t={});let i=new Array(t.axes?t.axes.length:e.axes.length),r=new Array(t.buttons?t.buttons.length:e.buttons.length),n=null;if(t.gripTransform){let e=t.gripTransform.orientation||[0,0,0,1];g(n=d(),N(e,e),t.gripTransform.position||[0,0,0])}let s=null;if(t.targetRayTransform){let e=t.targetRayTransform.orientation||[0,0,0,1];g(s=d(),N(e,e),t.targetRayTransform.position||[0,0,0])}this[At]={gamepad:e,map:t,id:t.id||e.id,mapping:t.mapping||e.mapping,axes:i,buttons:r,gripTransform:n,targetRayTransform:s},this._update()}_update(){let e=this[At].gamepad,t=this[At].map,i=this[At].axes;for(let r=0;r<i.length;++r)t.axes&&r in t.axes?null===t.axes[r]?i[r]=0:i[r]=e.axes[t.axes[r]]:i[r]=e.axes[r];let r=this[At].buttons;for(let i=0;i<r.length;++i)t.buttons&&i in t.buttons?null===t.buttons[i]?r[i]=ht:r[i]=e.buttons[t.buttons[i]]:r[i]=e.buttons[i]}get id(){return this[At].id}get index(){return 0}get connected(){return this[At].gamepad.connected}get timestamp(){return this[At].gamepad.timestamp}get mapping(){return this[At].mapping}get axes(){return this[At].axes}get buttons(){return this[At].buttons}}class dt{constructor(e,t=0){this.polyfill=e,this.nativeGamepad=null,this.gamepad=null,this.inputSource=new Ce(this),this.lastPosition=y(),this.emulatedPosition=!1,this.basePoseMatrix=d(),this.outputMatrix=d(),this.inputPoses=new WeakMap,this.primaryButtonIndex=t,this.primaryActionPressed=!1,this.handedness="",this.targetRayMode="gaze",this.armModel=null}updateFromGamepad(e){this.nativeGamepad!==e&&(this.nativeGamepad=e,this.gamepad=e?new ct(e,Ze[e.id]):null),this.handedness=e.hand,this.gamepad&&this.gamepad._update(),e.pose?(this.targetRayMode="tracked-pointer",this.emulatedPosition=!e.pose.hasPosition):""===e.hand&&(this.targetRayMode="gaze",this.emulatedPosition=!1)}updateBasePoseMatrix(){if(this.nativeGamepad&&this.nativeGamepad.pose){let e=this.nativeGamepad.pose,t=e.position,i=e.orientation;if(!t&&!i)return;t?(this.lastPosition[0]=t[0],this.lastPosition[1]=t[1],this.lastPosition[2]=t[2]):e.hasPosition?t=this.lastPosition:(this.armModel||(this.armModel=new lt),this.armModel.setHandedness(this.nativeGamepad.hand),this.armModel.update(i,this.polyfill.getBasePoseMatrix()),t=this.armModel.getPosition()),g(this.basePoseMatrix,i,t)}else u(this.basePoseMatrix,this.polyfill.getBasePoseMatrix());return this.basePoseMatrix}getXRPose(e,t){switch(this.updateBasePoseMatrix(),t){case"target-ray":e.transformBasePoseMatrix(this.outputMatrix,this.basePoseMatrix),this.gamepad&&this.gamepad[At].targetRayTransform&&m(this.outputMatrix,this.outputMatrix,this.gamepad[At].targetRayTransform);break;case"grip":if(!this.nativeGamepad||!this.nativeGamepad.pose)return null;e.transformBasePoseMatrix(this.outputMatrix,this.basePoseMatrix),this.gamepad&&this.gamepad[At].gripTransform&&m(this.outputMatrix,this.outputMatrix,this.gamepad[At].gripTransform);break;default:return null}return e._adjustForOriginOffset(this.outputMatrix),new XRPose(new XRRigidTransform(this.outputMatrix),this.emulatedPosition)}}const ut=!1,pt={highRefreshRate:!0},ft={oculus:1,openvr:1,"spatial controller (spatial interaction source)":1};let mt=0;class gt{constructor(e,t={}){if(this.mode=e,this.outputContext=null,this.immersive="immersive-vr"==e||"immersive-ar"==e,this.ended=null,this.baseLayer=null,this.inlineVerticalFieldOfView=.5*Math.PI,this.id=++mt,this.modifiedCanvasLayer=!1,this.outputContext&&!ut){const e=t.renderContextType||"2d";this.renderContext=this.outputContext.canvas.getContext(e)}}}class wt extends We{constructor(e,t){const{canPresent:i}=t.capabilities;super(e),this.display=t,this.frame=new e.VRFrameData,this.sessions=new Map,this.immersiveSession=null,this.canPresent=i,this.baseModelMatrix=d(),this.gamepadInputSources={},this.tempVec3=new Float32Array(3),this.onVRDisplayPresentChange=this.onVRDisplayPresentChange.bind(this),e.window.addEventListener("vrdisplaypresentchange",this.onVRDisplayPresentChange),this.CAN_USE_GAMEPAD=e.navigator&&"getGamepads"in e.navigator,this.HAS_BITMAP_SUPPORT=Qe(e)}get depthNear(){return this.display.depthNear}set depthNear(e){this.display.depthNear=e}get depthFar(){return this.display.depthFar}set depthFar(e){this.display.depthFar=e}onBaseLayerSet(e,t){const i=this.sessions.get(e),r=t.context.canvas;if(i.immersive){const e=this.display.getEyeParameters("left"),n=this.display.getEyeParameters("right");r.width=2*Math.max(e.renderWidth,n.renderWidth),r.height=Math.max(e.renderHeight,n.renderHeight),this.display.requestPresent([{source:r,attributes:pt}]).then(()=>{ut||this.global.document.body.contains(r)||(i.modifiedCanvasLayer=!0,this.global.document.body.appendChild(r),ke(r)),i.baseLayer=t})}else i.baseLayer=t}onInlineVerticalFieldOfViewSet(e,t){this.sessions.get(e).inlineVerticalFieldOfView=t}supportsSession(e){if(-1==n.indexOf(e))throw new TypeError(`The provided value '${e}' is not a valid enum value of type XRSessionMode`);return"immersive-ar"!=e&&("immersive-vr"!=e||!1!==this.canPresent)}async requestSession(e){if(!this.supportsSession(e))return Promise.reject();let t="immersive-vr"==e;if(t){const e=this.global.document.createElement("canvas");if(!ut){e.getContext("webgl")}await this.display.requestPresent([{source:e,attributes:pt}])}const i=new gt(e,{renderContextType:this.HAS_BITMAP_SUPPORT?"bitmaprenderer":"2d"});return this.sessions.set(i.id,i),t&&(this.immersiveSession=i,this.dispatchEvent("@@webxr-polyfill/vr-present-start",i.id)),Promise.resolve(i.id)}requestAnimationFrame(e){return this.display.requestAnimationFrame(e)}getPrimaryButtonIndex(e){let t=0,i=e.id.toLowerCase();for(let e in ft)if(i.includes(e)){t=ft[e];break}return Math.min(t,e.buttons.length-1)}onFrameStart(e){this.display.getFrameData(this.frame);const t=this.sessions.get(e);if(t.immersive&&this.CAN_USE_GAMEPAD){let e=this.gamepadInputSources;this.gamepadInputSources={};let i=this.global.navigator.getGamepads();for(let r=0;r<i.length;++r){let n=i[r];if(n&&n.displayId>0){let i=e[r];if(i||(i=new dt(this,this.getPrimaryButtonIndex(n))),i.updateFromGamepad(n),this.gamepadInputSources[r]=i,-1!=i.primaryButtonIndex){let e=n.buttons[i.primaryButtonIndex].pressed;e&&!i.primaryActionPressed?this.dispatchEvent("@@webxr-polyfill/input-select-start",{sessionId:t.id,inputSource:i.inputSource}):!e&&i.primaryActionPressed&&this.dispatchEvent("@@webxr-polyfill/input-select-end",{sessionId:t.id,inputSource:i.inputSource}),i.primaryActionPressed=e}}}}if(!ut&&!t.immersive&&t.baseLayer){const e=t.baseLayer.context.canvas;!function(e,t,i,r,n){let s,a=1/Math.tan(t/2);e[0]=a/i,e[1]=0,e[2]=0,e[3]=0,e[4]=0,e[5]=a,e[6]=0,e[7]=0,e[8]=0,e[9]=0,e[11]=-1,e[12]=0,e[13]=0,e[15]=0,null!=n&&n!==1/0?(s=1/(r-n),e[10]=(n+r)*s,e[14]=2*n*r*s):(e[10]=-1,e[14]=-2*r)}(this.frame.leftProjectionMatrix,t.inlineVerticalFieldOfView,e.width/e.height,this.depthNear,this.depthFar)}}onFrameEnd(e){const t=this.sessions.get(e);if(!t.ended&&t.baseLayer){if(t.outputContext&&(!t.immersive||this.display.capabilities.hasExternalDisplay)){const e=t.immersive&&this.display.capabilities.hasExternalDisplay,i=t.baseLayer.context.canvas,r=e?i.width/2:i.width,n=i.height;if(!ut){const e=t.outputContext.canvas,s=e.width,a=e.height,o=t.renderContext;this.HAS_BITMAP_SUPPORT?i.transferToImageBitmap?o.transferFromImageBitmap(i.transferToImageBitmap()):this.global.createImageBitmap(i,0,0,r,n,{resizeWidth:s,resizeHeight:a}).then(e=>o.transferFromImageBitmap(e)):o.drawImage(i,0,0,r,n,0,0,s,a)}}t.immersive&&t.baseLayer&&this.display.submitFrame()}}cancelAnimationFrame(e){this.display.cancelAnimationFrame(e)}async endSession(e){const t=this.sessions.get(e);if(!t.ended)return t.immersive?this.display.exitPresent():void(t.ended=!0)}requestStageBounds(){if(this.display.stageParameters){const e=this.display.stageParameters.sizeX,t=this.display.stageParameters.sizeZ,i=[];return i.push(-e/2),i.push(-t/2),i.push(e/2),i.push(-t/2),i.push(e/2),i.push(t/2),i.push(-e/2),i.push(t/2),i}return null}async requestFrameOfReferenceTransform(e,t){if("stage"===e&&this.display.stageParameters&&this.display.stageParameters.sittingToStandingTransform)return this.display.stageParameters.sittingToStandingTransform}getProjectionMatrix(e){if("left"===e)return this.frame.leftProjectionMatrix;if("right"===e)return this.frame.rightProjectionMatrix;if("none"===e)return this.frame.leftProjectionMatrix;throw new Error("eye must be of type 'left' or 'right'")}getViewport(e,t,i,r){const n=this.sessions.get(e),{width:s,height:a}=i.context.canvas;if(!n.immersive)return r.x=r.y=0,r.width=s,r.height=a,!0;if("left"===t)r.x=0;else{if("right"!==t)return!1;r.x=s/2}return r.y=0,r.width=s/2,r.height=a,!0}getBasePoseMatrix(){let{position:e,orientation:t}=this.frame.pose;return e||t?(e||((e=this.tempVec3)[0]=e[1]=e[2]=0),g(this.baseModelMatrix,t,e),this.baseModelMatrix):this.baseModelMatrix}getBaseViewMatrix(e){if("left"===e)return this.frame.leftViewMatrix;if("right"===e)return this.frame.rightViewMatrix;throw new Error("eye must be of type 'left' or 'right'")}getInputSources(){let e=[];for(let t in this.gamepadInputSources)e.push(this.gamepadInputSources[t].inputSource);return e}getInputPose(e,t,i){if(!t)return null;for(let r in this.gamepadInputSources){let n=this.gamepadInputSources[r];if(n.inputSource===e)return n.getXRPose(t,i)}return null}onWindowResize(){}onVRDisplayPresentChange(e){this.display.isPresenting||this.sessions.forEach(e=>{if(e.immersive&&!e.ended){if(e.modifiedCanvasLayer){const t=e.baseLayer.context.canvas;document.body.removeChild(t),t.setAttribute("style","")}this.immersiveSession===e&&(this.immersiveSession=null),this.dispatchEvent("@@webxr-polyfill/vr-present-end",e.id)}})}}const vt=async function(e,t){if(t.webvr){let t=await async function(e){let t=null;if("getVRDisplays"in e.navigator)try{const i=await e.navigator.getVRDisplays();i&&i.length&&(t=new wt(e,i[0]))}catch(e){}return t}(e);if(t)return t}return e.VRFrameData||(e.VRFrameData=function(){this.rightViewMatrix=new Float32Array(16),this.leftViewMatrix=new Float32Array(16),this.rightProjectionMatrix=new Float32Array(16),this.leftProjectionMatrix=new Float32Array(16),this.pose=null}),new class extends wt{constructor(e,t){const i=new Xe(t||{});super(e,i),this.display=i,this.frame={rightViewMatrix:new Float32Array(16),leftViewMatrix:new Float32Array(16),rightProjectionMatrix:new Float32Array(16),leftProjectionMatrix:new Float32Array(16),pose:null,timestamp:null}}}(e,t.cardboardConfig)},yt={global:e,webvr:!0,cardboard:!0,cardboardConfig:null,allowCardboardOnDesktop:!1},bt=["navigator","HTMLCanvasElement","WebGLRenderingContext"];return class{constructor(e={}){this.config=Object.freeze(Object.assign({},yt,e)),this.global=this.config.global,this.nativeWebXR="xr"in this.global.navigator,this.injected=!1,this.nativeWebXR?this.config.cardboard&&Ne(this.global)&&this._patchNavigatorXR():this._injectPolyfill(this.global)}_injectPolyfill(e){if(!bt.every(t=>!!e[t]))throw new Error(`Global must have the following attributes : ${bt}`);for(const t of Object.keys(Ie))void 0!==e[t]?console.warn(`${t} already defined on global.`):e[t]=Ie[t];Le(e.WebGLRenderingContext)&&(Oe(e.HTMLCanvasElement),e.OffscreenCanvas&&Oe(e.OffscreenCanvas),e.WebGL2RenderingContext&&Le(e.WebGL2RenderingContext)),this.injected=!0,this._patchNavigatorXR()}_patchNavigatorXR(){let e=vt(this.global,this.config);this.xr=new XR(e),Object.defineProperty(this.global.navigator,"xr",{value:this.xr,configurable:!0})}}});
diff --git a/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.module.js b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.module.js new file mode 100644 index 0000000..5afcfbc --- /dev/null +++ b/third_party/webxr_test_pages/webxr-samples/js/webxr-polyfill.module.js
@@ -0,0 +1,6129 @@ +/** + * @license + * webxr-polyfill + * Copyright (c) 2017 Google + * 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. + */ + +/** + * @license + * cardboard-vr-display + * Copyright (c) 2015-2017 Google + * 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. + */ + +/** + * @license + * webvr-polyfill-dpdb + * Copyright (c) 2017 Google + * 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. + */ + +/** + * @license + * wglu-preserve-state + * Copyright (c) 2016, Brandon Jones. + * + * 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. + */ + +/** + * @license + * nosleep.js + * Copyright (c) 2017, Rich Tibbett + * + * 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. + */ + +const _global = typeof global !== 'undefined' ? global : + typeof self !== 'undefined' ? self : + typeof window !== 'undefined' ? window : {}; + +const PRIVATE = Symbol('@@webxr-polyfill/EventTarget'); +class EventTarget { + constructor() { + this[PRIVATE] = { + listeners: new Map(), + }; + } + addEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + typedListeners.push(listener); + this[PRIVATE].listeners.set(type, typedListeners); + } + removeEventListener(type, listener) { + if (typeof type !== 'string') { throw new Error('`type` must be a string'); } + if (typeof listener !== 'function') { throw new Error('`listener` must be a function'); } + const typedListeners = this[PRIVATE].listeners.get(type) || []; + for (let i = typedListeners.length; i >= 0; i--) { + if (typedListeners[i] === listener) { + typedListeners.pop(); + } + } + } + dispatchEvent(type, event) { + const typedListeners = this[PRIVATE].listeners.get(type) || []; + const queue = []; + for (let i = 0; i < typedListeners.length; i++) { + queue[i] = typedListeners[i]; + } + for (let listener of queue) { + listener(event); + } + if (typeof this[`on${type}`] === 'function') { + this[`on${type}`](event); + } + } +} + +const PRIVATE$1 = Symbol('@@webxr-polyfill/XR'); +const XRSessionModes = ['inline', 'immersive-vr', 'immersive-ar']; +const POLYFILL_REQUEST_SESSION_ERROR = +`Polyfill Error: Must call navigator.xr.supportsSession() with any XRSessionMode +or navigator.xr.requestSession('inline') prior to requesting an immersive +session. This is a limitation specific to the WebXR Polyfill and does not apply +to native implementations of the API.`; +class XR$1 extends EventTarget { + constructor(devicePromise) { + super(); + this[PRIVATE$1] = { + device: null, + devicePromise, + immersiveSession: null, + inlineSessions: new Set(), + }; + devicePromise.then((device) => { this[PRIVATE$1].device = device; }); + } + async supportsSession(mode) { + if (!this[PRIVATE$1].device) { + await this[PRIVATE$1].devicePromise; + } + if (mode != 'inline') { + if (!this[PRIVATE$1].device.supportsSession(mode)) { + return Promise.reject(null); + } + } + return Promise.resolve(null); + } + async requestSession(mode) { + if (!this[PRIVATE$1].device) { + if (mode != 'inline') { + throw new Error(POLYFILL_REQUEST_SESSION_ERROR); + } else { + await this[PRIVATE$1].devicePromise; + } + } + const sessionId = await this[PRIVATE$1].device.requestSession(mode); + const session = new XRSession(this[PRIVATE$1].device, mode, sessionId); + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.add(session); + } else { + this[PRIVATE$1].immersiveSession = session; + } + const onSessionEnd = () => { + if (mode == 'inline') { + this[PRIVATE$1].inlineSessions.delete(session); + } else { + this[PRIVATE$1].immersiveSession = null; + } + session.removeEventListener('end', onSessionEnd); + }; + session.addEventListener('end', onSessionEnd); + return session; + } +} + +let now; +if ('performance' in _global === false) { + let startTime = Date.now(); + now = () => Date.now() - startTime; +} else { + now = () => performance.now(); +} +var now$1 = now; + +const PRIVATE$2 = Symbol('@@webxr-polyfill/XRPose'); +class XRPose$1 { + constructor(transform, emulatedPosition) { + this[PRIVATE$2] = { + transform, + emulatedPosition, + }; + } + get transform() { return this[PRIVATE$2].transform; } + get emulatedPosition() { return this[PRIVATE$2].emulatedPosition; } + _setTransform(transform) { this[PRIVATE$2].transform = transform; } +} + +const EPSILON = 0.000001; +let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + + +const degree = Math.PI / 180; + +function create() { + let out = new ARRAY_TYPE(16); + if(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; +} + +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; +} + + +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; +} + +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; + 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; +} + + +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]; + 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; +} + + + + + + + + + + + + +function fromRotationTranslation(out, q, v) { + 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; +} + +function getTranslation(out, mat) { + out[0] = mat[12]; + out[1] = mat[13]; + out[2] = mat[14]; + return out; +} + +function getRotation(out, mat) { + 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; +} + + + + +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; +} + +function create$1() { + let out = new ARRAY_TYPE(3); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} +function clone$1(a) { + var out = new ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} +function length(a) { + let x = a[0]; + let y = a[1]; + let z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +} +function fromValues$1(x, y, z) { + let out = new ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} +function copy$1(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} + +function add$1(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +} + + + + + + + + +function scale$1(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return 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) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +} +function dot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +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; +} + + + + + + +function transformQuat(out, a, q) { + let qx = q[0], qy = q[1], qz = q[2], qw = q[3]; + let x = a[0], y = a[1], z = a[2]; + let uvx = qy * z - qz * y, + uvy = qz * x - qx * z, + uvz = qx * y - qy * x; + let uuvx = qy * uvz - qz * uvy, + uuvy = qz * uvx - qx * uvz, + uuvz = qx * uvy - qy * uvx; + let w2 = qw * 2; + uvx *= w2; + uvy *= w2; + uvz *= w2; + uuvx *= 2; + uuvy *= 2; + uuvz *= 2; + out[0] = x + uvx + uuvx; + out[1] = y + uvy + uuvy; + out[2] = z + uvz + uuvz; + return out; +} + + + +function angle(a, b) { + let tempA = fromValues$1(a[0], a[1], a[2]); + let tempB = fromValues$1(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); + } +} + + + + + + + + +const len = length; + +const forEach = (function() { + let vec = create$1(); + 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; + }; +})(); + +function create$2() { + let out = new ARRAY_TYPE(9); + if(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; +} + +function create$3() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} +function clone$3(a) { + let out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} +function fromValues$3(x, y, z, w) { + let out = new ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +} +function copy$3(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$1(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; +} + + + + + + + + + + + + + + + +const forEach$1 = (function() { + let vec = create$3(); + 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; + }; +})(); + +function create$4() { + let out = new ARRAY_TYPE(4); + if(ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return 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; +} + +function multiply$4(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; +} + + + + +function slerp(out, a, b, t) { + 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; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + if ( (1.0 - cosom) > EPSILON ) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + 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; +} + +function invert$2(out, a) { + let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + let dot$$1 = a0*a0 + a1*a1 + a2*a2 + a3*a3; + let invDot = dot$$1 ? 1.0/dot$$1 : 0; + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +} + +function fromMat3(out, m) { + let fTrace = m[0] + m[4] + m[8]; + let fRoot; + if ( fTrace > 0.0 ) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; + out[0] = (m[5]-m[7])*fRoot; + out[1] = (m[6]-m[2])*fRoot; + out[2] = (m[1]-m[3])*fRoot; + } else { + 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; +} +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; +} + +const clone$4 = clone$3; +const fromValues$4 = fromValues$3; +const copy$4 = copy$3; + + + + + + + + + + +const normalize$2 = normalize$1; + + +const rotationTo = (function() { + let tmpvec3 = create$1(); + let xUnitVec3 = fromValues$1(1,0,0); + let yUnitVec3 = fromValues$1(0,1,0); + return function(out, a, b) { + let dot$$1 = dot(a, b); + if (dot$$1 < -0.999999) { + cross(tmpvec3, xUnitVec3, a); + if (len(tmpvec3) < 0.000001) + cross(tmpvec3, yUnitVec3, a); + normalize(tmpvec3, tmpvec3); + setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot$$1 > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot$$1; + return normalize$2(out, out); + } + }; +})(); +const sqlerp = (function () { + let temp1 = create$4(); + let temp2 = create$4(); + 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; + }; +}()); +const setAxes = (function() { + let matr = create$2(); + 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$2(out, fromMat3(out, matr)); + }; +})(); + +const PRIVATE$3 = Symbol('@@webxr-polyfill/XRRigidTransform'); +class XRRigidTransform$1 { + constructor() { + this[PRIVATE$3] = { + matrix: null, + position: null, + orientation: null, + inverse: null, + }; + if (arguments.length === 0) { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + } else if (arguments.length === 1) { + if (arguments[0] instanceof Float32Array) { + this[PRIVATE$3].matrix = arguments[0]; + } else { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: 0, y: 0, z: 0, w: 1 + }); + } + } else if (arguments.length === 2) { + this[PRIVATE$3].position = this._getPoint(arguments[0]); + this[PRIVATE$3].orientation = this._getPoint(arguments[1]); + } else { + throw new Error("Too many arguments!"); + } + if (this[PRIVATE$3].matrix) { + let position = create$1(); + getTranslation(position, this[PRIVATE$3].matrix); + this[PRIVATE$3].position = DOMPointReadOnly.fromPoint({ + x: position[0], + y: position[1], + z: position[2] + }); + let orientation = create$4(); + getRotation(orientation, this[PRIVATE$3].matrix); + this[PRIVATE$3].orientation = DOMPointReadOnly.fromPoint({ + x: orientation[0], + y: orientation[1], + z: orientation[2], + w: orientation[3] + }); + } else { + this[PRIVATE$3].matrix = identity(new Float32Array(16)); + fromRotationTranslation( + this[PRIVATE$3].matrix, + fromValues$4( + this[PRIVATE$3].orientation.x, + this[PRIVATE$3].orientation.y, + this[PRIVATE$3].orientation.z, + this[PRIVATE$3].orientation.w), + fromValues$1( + this[PRIVATE$3].position.x, + this[PRIVATE$3].position.y, + this[PRIVATE$3].position.z) + ); + } + } + _getPoint(arg) { + if (arg instanceof DOMPointReadOnly) { + return arg; + } + return DOMPointReadOnly.fromPoint(arg); + } + get matrix() { return this[PRIVATE$3].matrix; } + get position() { return this[PRIVATE$3].position; } + get orientation() { return this[PRIVATE$3].orientation; } + get inverse() { + if (this[PRIVATE$3].inverse === null) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, this[PRIVATE$3].matrix); + this[PRIVATE$3].inverse = new XRRigidTransform$1(invMatrix); + this[PRIVATE$3].inverse[PRIVATE$3].inverse = this; + } + return this[PRIVATE$3].inverse; + } +} + +const PRIVATE$4 = Symbol('@@webxr-polyfill/XRViewerPose'); +class XRViewerPose extends XRPose$1 { + constructor(device) { + super(new XRRigidTransform$1(), false); + this[PRIVATE$4] = { + device, + leftViewMatrix: identity(new Float32Array(16)), + rightViewMatrix: identity(new Float32Array(16)), + poseModelMatrix: identity(new Float32Array(16)), + }; + } + get poseModelMatrix() { return this[PRIVATE$4].poseModelMatrix; } + getViewMatrix(view) { + switch (view.eye) { + case 'left': return this[PRIVATE$4].leftViewMatrix; + case 'right': return this[PRIVATE$4].rightViewMatrix; + } + throw new Error(`view is not a valid XREye`); + } + get views() { + return this[PRIVATE$4].views; + } + set views(value) { + this[PRIVATE$4].views = value; + } + updateFromReferenceSpace(refSpace) { + const pose = this[PRIVATE$4].device.getBasePoseMatrix(); + const leftViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('left'); + const rightViewMatrix = this[PRIVATE$4].device.getBaseViewMatrix('right'); + if (pose) { + refSpace.transformBasePoseMatrix(this[PRIVATE$4].poseModelMatrix, pose); + refSpace._adjustForOriginOffset(this[PRIVATE$4].poseModelMatrix); + super._setTransform(new XRRigidTransform$1(this[PRIVATE$4].poseModelMatrix)); + } + if (leftViewMatrix && rightViewMatrix) { + refSpace.transformBaseViewMatrix(this[PRIVATE$4].leftViewMatrix, + leftViewMatrix, + this[PRIVATE$4].poseModelMatrix); + refSpace.transformBaseViewMatrix(this[PRIVATE$4].rightViewMatrix, + rightViewMatrix, + this[PRIVATE$4].poseModelMatrix); + multiply(this[PRIVATE$4].leftViewMatrix, this[PRIVATE$4].leftViewMatrix, refSpace._originOffsetMatrix()); + multiply(this[PRIVATE$4].rightViewMatrix, this[PRIVATE$4].rightViewMatrix, refSpace._originOffsetMatrix()); + } + for (let view of this[PRIVATE$4].views) { + if (view.eye == "left") { + view._updateViewMatrix(this[PRIVATE$4].leftViewMatrix); + } else if (view.eye == "right") { + view._updateViewMatrix(this[PRIVATE$4].rightViewMatrix); + } + } + } +} + +const PRIVATE$5 = Symbol('@@webxr-polyfill/XRViewport'); +class XRViewport { + constructor(target) { + this[PRIVATE$5] = { target }; + } + get x() { return this[PRIVATE$5].target.x; } + get y() { return this[PRIVATE$5].target.y; } + get width() { return this[PRIVATE$5].target.width; } + get height() { return this[PRIVATE$5].target.height; } +} + +const XREyes = ['left', 'right']; +const PRIVATE$6 = Symbol('@@webxr-polyfill/XRView'); +class XRView { + constructor(device, eye, sessionId) { + if (!XREyes.includes(eye)) { + throw new Error(`XREye must be one of: ${XREyes}`); + } + const temp = Object.create(null); + const viewport = new XRViewport(temp); + this[PRIVATE$6] = { + device, + eye, + viewport, + temp, + sessionId, + transform: null, + }; + } + get eye() { return this[PRIVATE$6].eye; } + get projectionMatrix() { return this[PRIVATE$6].device.getProjectionMatrix(this.eye); } + get transform() { return this[PRIVATE$6].transform; } + _updateViewMatrix(viewMatrix) { + let invMatrix = identity(new Float32Array(16)); + invert(invMatrix, viewMatrix); + this[PRIVATE$6].transform = new XRRigidTransform$1(invMatrix); + } + _getViewport(layer) { + const viewport = this[PRIVATE$6].viewport; + if (this[PRIVATE$6].device.getViewport(this[PRIVATE$6].sessionId, + this.eye, + layer, + this[PRIVATE$6].temp)) { + return this[PRIVATE$6].viewport; + } + return undefined; + } +} + +var EPSILON$1 = 0.000001; +var ARRAY_TYPE$1 = typeof Float32Array !== 'undefined' ? Float32Array : Array; + + +var degree$1 = Math.PI / 180; + +function create$7() { + var out = new ARRAY_TYPE$1(9); + if (ARRAY_TYPE$1 != 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; +} + +function create$9() { + var out = new ARRAY_TYPE$1(3); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} + +function length$3(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return Math.sqrt(x * x + y * y + z * z); +} +function fromValues$9(x, y, z) { + var out = new ARRAY_TYPE$1(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} + + + + + + + + + + + + + + + + + + +function normalize$3(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var len = x * x + y * y + z * z; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +} +function dot$3(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +function cross$1(out, a, b) { + var ax = a[0], + ay = a[1], + az = a[2]; + var 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; +} + + + + + + + + + + + + + + + + + + + +var len$3 = length$3; + +var forEach$2 = function () { + var vec = create$9(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + 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; + }; +}(); + +function create$10() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + +function normalize$4(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + var 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; +} + + + + + + + + + + + + + + + +var forEach$3 = function () { + var vec = create$10(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + 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; + }; +}(); + +function create$11() { + var out = new ARRAY_TYPE$1(4); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return out; +} + +function setAxisAngle$1(out, axis, rad) { + rad = rad * 0.5; + var 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; +} + + + + + + +function slerp$1(out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = b[0], + by = b[1], + bz = b[2], + bw = b[3]; + var omega = void 0, + cosom = void 0, + sinom = void 0, + scale0 = void 0, + scale1 = void 0; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if (cosom < 0.0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } + if (1.0 - cosom > EPSILON$1) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1.0 - t; + scale1 = t; + } + 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; +} + + + +function fromMat3$1(out, m) { + var fTrace = m[0] + m[4] + m[8]; + var fRoot = void 0; + if (fTrace > 0.0) { + fRoot = Math.sqrt(fTrace + 1.0); + out[3] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[0] = (m[5] - m[7]) * fRoot; + out[1] = (m[6] - m[2]) * fRoot; + out[2] = (m[1] - m[3]) * fRoot; + } else { + var i = 0; + if (m[4] > m[0]) i = 1; + if (m[8] > m[i * 3 + i]) i = 2; + var j = (i + 1) % 3; + var 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; +} + + + + + + + + + + + + + + + +var normalize$5 = normalize$4; + + +var rotationTo$1 = function () { + var tmpvec3 = create$9(); + var xUnitVec3 = fromValues$9(1, 0, 0); + var yUnitVec3 = fromValues$9(0, 1, 0); + return function (out, a, b) { + var dot = dot$3(a, b); + if (dot < -0.999999) { + cross$1(tmpvec3, xUnitVec3, a); + if (len$3(tmpvec3) < 0.000001) cross$1(tmpvec3, yUnitVec3, a); + normalize$3(tmpvec3, tmpvec3); + setAxisAngle$1(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 { + cross$1(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return normalize$5(out, out); + } + }; +}(); +var sqlerp$1 = function () { + var temp1 = create$11(); + var temp2 = create$11(); + return function (out, a, b, c, d, t) { + slerp$1(temp1, a, d, t); + slerp$1(temp2, b, c, t); + slerp$1(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; +}(); +var setAxes$1 = function () { + var matr = create$7(); + 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$5(out, fromMat3$1(out, matr)); + }; +}(); + +function create$13() { + var out = new ARRAY_TYPE$1(2); + if (ARRAY_TYPE$1 != Float32Array) { + out[0] = 0; + out[1] = 0; + } + return out; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var forEach$4 = function () { + var vec = create$13(); + return function (a, stride, offset, count, fn, arg) { + var i = void 0, + l = void 0; + 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; + }; +}(); + +const PRIVATE$7 = Symbol('@@webxr-polyfill/XRFrame'); +class XRFrame { + constructor(device, session, sessionId) { + const viewerPose = new XRViewerPose(device); + const views = [ + new XRView(device, 'left', sessionId), + ]; + if (session.immersive) { + views.push(new XRView(device, 'right', sessionId)); + } + viewerPose.views = views; + this[PRIVATE$7] = { + device, + viewerPose, + views, + session, + }; + } + get session() { return this[PRIVATE$7].session; } + get views() { return this[PRIVATE$7].views; } + getViewerPose(space) { + this[PRIVATE$7].viewerPose.updateFromReferenceSpace(space); + return this[PRIVATE$7].viewerPose; + } + getPose(space, baseSpace) { + if (space._specialType === "viewer") { + let viewerPose = this.getViewerPose(baseSpace); + return new XRPose( + new XRRigidTransform(viewerPose.poseModelMatrix), + viewerPose.emulatedPosition); + } + if (space._specialType === "target-ray" || space._specialType === "grip") { + return this[PRIVATE$7].device.getInputPose( + space._inputSource, baseSpace, space._specialType); + } + return null; + } +} + +const PRIVATE$8 = Symbol('@@webxr-polyfill/XRStageBoundsPoint'); +class XRStageBoundsPoint { + constructor(x, z) { + this[PRIVATE$8] = { x, z }; + } + get x() { return this[PRIVATE$8].x; } + get z() { return this[PRIVATE$8].z; } +} + +const PRIVATE$9 = Symbol('@@webxr-polyfill/XRStageBounds'); +class XRStageBounds { + constructor(boundsData) { + const geometry = []; + for (let i = 0; i < boundsData.length; i += 2) { + geometry.push(new XRStageBoundsPoint(boundsData[i], boundsData[i + 1])); + } + this[PRIVATE$9] = { geometry }; + } + get geometry() { return this[PRIVATE$9].geometry; } +} + +const PRIVATE$10 = Symbol('@@webxr-polyfill/XRSpace'); +class XRSpace { + constructor(specialType = null, inputSource = null) { + this[PRIVATE$10] = { + specialType, + inputSource, + }; + } + get _specialType() { + return this[PRIVATE$10].specialType; + } + get _inputSource() { + return this[PRIVATE$10].inputSource; + } +} + +const DEFAULT_EMULATION_HEIGHT = 1.6; +const PRIVATE$11 = Symbol('@@webxr-polyfill/XRReferenceSpace'); +const XRReferenceSpaceTypes = [ + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded' +]; +const XRReferenceSpaceOptions = Object.freeze({ + disableStageEmulation: false, + stageEmulationHeight: 0, +}); +class XRReferenceSpace extends XRSpace { + constructor(device, type, options, transform, bounds) { + options = Object.assign({}, XRReferenceSpaceOptions, options); + if (!XRReferenceSpaceTypes.includes(type)) { + throw new Error(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + super((type === 'viewer') ? 'viewer' : null); + if (this._isFloor(type) && options.disableStageEmulation && !transform) { + throw new Error(`XRReferenceSpace cannot use 'bounded-floor' type, if disabling emulation and platform does not provide`); + } + const { disableStageEmulation, stageEmulationHeight } = options; + let emulatedHeight = 0; + if (this._isFloor(type) && !transform) { + emulatedHeight = stageEmulationHeight !== 0 ? stageEmulationHeight : DEFAULT_EMULATION_HEIGHT; + } + if (this._isFloor(type) && !transform) { + transform = identity(new Float32Array(16)); + transform[13] = emulatedHeight; + } + if (!transform) { + transform = identity(new Float32Array(16)); + } + this[PRIVATE$11] = { + disableStageEmulation, + stageEmulationHeight, + emulatedHeight, + type, + transform, + device, + bounds, + options, + originOffset : identity(new Float32Array(16)), + }; + this.onboundschange = undefined; + } + _isFloor(type) { + return type === 'bounded-floor' || type === 'local-floor'; + } + get bounds() { return this[PRIVATE$11].bounds; } + get emulatedHeight() { return this[PRIVATE$11].emulatedHeight; } + get type() { return this[PRIVATE$11].type; } + transformBasePoseMatrix(out, pose) { + if (this[PRIVATE$11].transform) { + multiply(out, this[PRIVATE$11].transform, pose); + return; + } + switch (this.type) { + case 'local': + if (out !== pose) { + copy(out, pose); + } + return; + } + } + transformBaseViewMatrix(out, view) { + let frameOfRef = this[PRIVATE$11].transform; + if (frameOfRef) { + invert(out, frameOfRef); + multiply(out, view, out); + } + else { + copy(out, view); + } + return out; + } + _inverseOriginOffsetMatrix() { + let result = identity(new Float32Array(16)); + invert(result, this[PRIVATE$11].originOffset); + return result; + } + _originOffsetMatrix() { + return this[PRIVATE$11].originOffset; + } + _adjustForOriginOffset(transformMatrix) { + multiply(transformMatrix, this._inverseOriginOffsetMatrix(), transformMatrix); + } + getOffsetReferenceSpace(additionalOffset) { + let newSpace = new XRReferenceSpace( + this[PRIVATE$11].device, + this[PRIVATE$11].type, + this[PRIVATE$11].options, + this[PRIVATE$11].transform, + this[PRIVATE$11].bounds); + multiply(newSpace[PRIVATE$11].originOffset, this[PRIVATE$11].originOffset, additionalOffset.matrix); + return newSpace; + } +} + +const POLYFILLED_XR_COMPATIBLE = Symbol('@@webxr-polyfill/polyfilled-xr-compatible'); +const XR_COMPATIBLE = Symbol('@@webxr-polyfill/xr-compatible'); + +const PRIVATE$12 = Symbol('@@webxr-polyfill/XRWebGLLayer'); +const XRWebGLLayerInit = Object.freeze({ + antialias: true, + depth: false, + stencil: false, + alpha: true, + multiview: false, + ignoreDepthValues: false, + framebufferScaleFactor: 1.0, +}); +class XRWebGLLayer { + constructor(session, context, layerInit={}) { + const config = Object.assign({}, XRWebGLLayerInit, layerInit); + if (!(session instanceof XRSession$1)) { + throw new Error('session must be a XRSession'); + } + if (session.ended) { + throw new Error(`InvalidStateError`); + } + if (context[POLYFILLED_XR_COMPATIBLE]) { + if (context[XR_COMPATIBLE] !== true) { + throw new Error(`InvalidStateError`); + } + } + const framebuffer = context.getParameter(context.FRAMEBUFFER_BINDING); + this[PRIVATE$12] = { + context, + config, + framebuffer, + session, + }; + } + get context() { return this[PRIVATE$12].context; } + get antialias() { return this[PRIVATE$12].config.antialias; } + get ignoreDepthValues() { return true; } + get framebuffer() { return this[PRIVATE$12].framebuffer; } + get framebufferWidth() { return this[PRIVATE$12].context.drawingBufferWidth; } + get framebufferHeight() { return this[PRIVATE$12].context.drawingBufferHeight; } + get _session() { return this[PRIVATE$12].session; } + getViewport(view) { + return view._getViewport(this); + } +} + +const PRIVATE$13 = Symbol('@@webxr-polyfill/XRSession'); +class XRSession$1 extends EventTarget { + constructor(device, mode, id) { + super(); + let immersive = mode != 'inline'; + let outputContext = null; + this[PRIVATE$13] = { + device, + mode, + immersive, + outputContext, + ended: false, + suspended: false, + suspendedCallback: null, + id, + activeRenderState: null, + pendingRenderState: null, + }; + const frame = new XRFrame(device, this, this[PRIVATE$13].id); + this[PRIVATE$13].frame = frame; + this[PRIVATE$13].onPresentationEnd = sessionId => { + if (sessionId !== this[PRIVATE$13].id) { + this[PRIVATE$13].suspended = false; + this.dispatchEvent('focus', { session: this }); + const suspendedCallback = this[PRIVATE$13].suspendedCallback; + this[PRIVATE$13].suspendedCallback = null; + if (suspendedCallback) { + this.requestAnimationFrame(suspendedCallback); + } + return; + } + this[PRIVATE$13].ended = true; + device.removeEventListener('@webvr-polyfill/vr-present-end', this[PRIVATE$13].onPresentationEnd); + device.removeEventListener('@webvr-polyfill/vr-present-start', this[PRIVATE$13].onPresentationStart); + device.removeEventListener('@@webvr-polyfill/input-select-start', this[PRIVATE$13].onSelectStart); + device.removeEventListener('@@webvr-polyfill/input-select-end', this[PRIVATE$13].onSelectEnd); + this.dispatchEvent('end', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-end', this[PRIVATE$13].onPresentationEnd); + this[PRIVATE$13].onPresentationStart = sessionId => { + if (sessionId === this[PRIVATE$13].id) { + return; + } + this[PRIVATE$13].suspended = true; + this.dispatchEvent('blur', { session: this }); + }; + device.addEventListener('@@webxr-polyfill/vr-present-start', this[PRIVATE$13].onPresentationStart); + this[PRIVATE$13].onSelectStart = evt => { + if (evt.sessionId !== this[PRIVATE$13].id) { + return; + } + this.dispatchEvent('selectstart', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-start', this[PRIVATE$13].onSelectStart); + this[PRIVATE$13].onSelectEnd = evt => { + if (evt.sessionId !== this[PRIVATE$13].id) { + return; + } + this.dispatchEvent('selectend', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + this.dispatchEvent('select', { + frame: this[PRIVATE$13].frame, + inputSource: evt.inputSource + }); + }; + device.addEventListener('@@webxr-polyfill/input-select-end', this[PRIVATE$13].onSelectEnd); + this.onblur = undefined; + this.onfocus = undefined; + this.onresetpose = undefined; + this.onend = undefined; + this.onselect = undefined; + this.onselectstart = undefined; + this.onselectend = undefined; + } + get renderState() { return this[PRIVATE$13].activeRenderState; } + get immersive() { return this[PRIVATE$13].immersive; } + get outputContext() { return this[PRIVATE$13].outputContext; } + get depthNear() { return this[PRIVATE$13].device.depthNear; } + set depthNear(value) { this[PRIVATE$13].device.depthNear = value; } + get depthFar() { return this[PRIVATE$13].device.depthFar; } + set depthFar(value) { this[PRIVATE$13].device.depthFar = value; } + get environmentBlendMode() { + return this[PRIVATE$13].device.environmentBlendMode || 'opaque'; + } + get baseLayer() { return this[PRIVATE$13].baseLayer; } + set baseLayer(value) { + if (this[PRIVATE$13].ended) { + return; + } + this[PRIVATE$13].baseLayer = value; + this[PRIVATE$13].device.onBaseLayerSet(this[PRIVATE$13].id, value); + } + async requestReferenceSpace(type, options={}) { + if (this[PRIVATE$13].ended) { + return; + } + options = Object.assign({}, XRReferenceSpaceOptions, options); + if (!XRReferenceSpaceTypes.includes(type)) { + throw new TypeError(`XRReferenceSpaceType must be one of ${XRReferenceSpaceTypes}`); + } + let transform = null; + let bounds = null; + try { + transform = await this[PRIVATE$13].device.requestFrameOfReferenceTransform(type, options); + } catch (e) { + if (type !== 'stage' || options.disableStageEmulation) { + throw e; + } + } + if (type === 'stage' && transform) { + bounds = this[PRIVATE$13].device.requestStageBounds(); + if (bounds) { + bounds = new XRStageBounds(bounds); + } + } + return new XRReferenceSpace(this[PRIVATE$13].device, type, options, transform, bounds); + } + requestAnimationFrame(callback) { + if (this[PRIVATE$13].ended) { + return; + } + if (this[PRIVATE$13].suspended && this[PRIVATE$13].suspendedCallback) { + return; + } + if (this[PRIVATE$13].suspended && !this[PRIVATE$13].suspendedCallback) { + this[PRIVATE$13].suspendedCallback = callback; + } + return this[PRIVATE$13].device.requestAnimationFrame(() => { + if (this[PRIVATE$13].pendingRenderState !== null) { + this[PRIVATE$13].activeRenderState = this[PRIVATE$13].pendingRenderState; + this[PRIVATE$13].pendingRenderState = null; + if (this[PRIVATE$13].activeRenderState.baseLayer) { + this[PRIVATE$13].device.onBaseLayerSet( + this[PRIVATE$13].id, + this[PRIVATE$13].activeRenderState.baseLayer); + } + if (this[PRIVATE$13].activeRenderState.inlineVerticalFieldOfView) { + this[PRIVATE$13].device.onInlineVerticalFieldOfViewSet( + this[PRIVATE$13].id, + this[PRIVATE$13].activeRenderState.inlineVerticalFieldOfView); + } + } + this[PRIVATE$13].device.onFrameStart(this[PRIVATE$13].id); + callback(now$1(), this[PRIVATE$13].frame); + this[PRIVATE$13].device.onFrameEnd(this[PRIVATE$13].id); + }); + } + cancelAnimationFrame(handle) { + if (this[PRIVATE$13].ended) { + return; + } + this[PRIVATE$13].device.cancelAnimationFrame(handle); + } + get inputSources() { + return this[PRIVATE$13].device.getInputSources(); + } + async end() { + if (this[PRIVATE$13].ended) { + return; + } + if (!this.immersive) { + this[PRIVATE$13].ended = true; + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/vr-present-start', + this[PRIVATE$13].onPresentationStart); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/vr-present-end', + this[PRIVATE$13].onPresentationEnd); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/input-select-start', + this[PRIVATE$13].onSelectStart); + this[PRIVATE$13].device.removeEventListener('@@webvr-polyfill/input-select-end', + this[PRIVATE$13].onSelectEnd); + this.dispatchEvent('end', { session: this }); + } + return this[PRIVATE$13].device.endSession(this[PRIVATE$13].id); + } + updateRenderState(newState) { + if (this[PRIVATE$13].ended) { + const message = "Can't call updateRenderState on an XRSession " + + "that has already ended."; + throw new Error(message); + } + if (newState.baseLayer && (newState.baseLayer._session !== this)) { + const message = "Called updateRenderState with a base layer that was " + + "created by a different session."; + throw new Error(message); + } + const fovSet = (newState.inlineVerticalFieldOfView !== null) && + (newState.inlineVerticalFieldOfView !== undefined); + if (fovSet) { + if (this[PRIVATE$13].immersive) { + const message = "inlineVerticalFieldOfView must not be set for an " + + "XRRenderState passed to updateRenderState for an " + + "immersive session."; + throw new Error(message); + } else { + newState.inlineVerticalFieldOfView = Math.min( + 3.13, Math.max(0.01, newState.inlineVerticalFieldOfView)); + } + } + if (this[PRIVATE$13].pendingRenderState === null) { + this[PRIVATE$13].pendingRenderState = Object.assign( + {}, this[PRIVATE$13].activeRenderState, newState); + } + } +} + +const PRIVATE$14 = Symbol('@@webxr-polyfill/XRInputSource'); +class XRInputSource { + constructor(impl) { + this[PRIVATE$14] = { + impl, + gripSpace: new XRSpace("grip", this), + targetRaySpace: new XRSpace("target-ray", this) + }; + } + get handedness() { return this[PRIVATE$14].impl.handedness; } + get targetRayMode() { return this[PRIVATE$14].impl.targetRayMode; } + get gripSpace() { + let mode = this[PRIVATE$14].impl.targetRayMode; + if (mode === "gaze" || mode === "screen") { + return null; + } + return this[PRIVATE$14].gripSpace; + } + get targetRaySpace() { return this[PRIVATE$14].targetRaySpace; } + get gamepad() { return this[PRIVATE$14].impl.gamepad; } +} + +const PRIVATE$15 = Symbol('@@webxr-polyfill/XRRenderState'); +const XRRenderStateInit = Object.freeze({ + depthNear: 0.1, + depthFar: 1000.0, + inlineVerticalFieldOfView: null, + baseLayer: null +}); +class XRRenderState { + constructor(stateInit = {}) { + const config = Object.assign({}, XRRenderStateInit, stateInit); + this[PRIVATE$15] = { config }; + } + get depthNear() { return this[PRIVATE$15].depthNear; } + get depthFar() { return this[PRIVATE$15].depthFar; } + get inlineVerticalFieldOfView() { return this[PRIVATE$15].inlineVerticalFieldOfView; } + get baseLayer() { return this[PRIVATE$15].baseLayer; } +} + +var API = { + XR: XR$1, + XRSession: XRSession$1, + XRFrame, + XRView, + XRViewport, + XRViewerPose, + XRWebGLLayer, + XRSpace, + XRReferenceSpace, + XRStageBounds, + XRStageBoundsPoint, + XRInputSource, + XRRenderState, + XRRigidTransform: XRRigidTransform$1, + XRPose: XRPose$1, +}; + +const polyfillMakeXRCompatible = Context => { + if (typeof Context.prototype.makeXRCompatible === 'function') { + return false; + } + Context.prototype.makeXRCompatible = function () { + this[XR_COMPATIBLE] = true; + return Promise.resolve(); + }; + return true; +}; +const polyfillGetContext = (Canvas) => { + const getContext = Canvas.prototype.getContext; + Canvas.prototype.getContext = function (contextType, glAttribs) { + const ctx = getContext.call(this, contextType, glAttribs); + ctx[POLYFILLED_XR_COMPATIBLE] = true; + if (glAttribs && ('xrCompatible' in glAttribs)) { + ctx[XR_COMPATIBLE] = glAttribs.xrCompatible; + } + return ctx; + }; +}; + +const isImageBitmapSupported = global => + !!(global.ImageBitmapRenderingContext && + global.createImageBitmap); +const isMobile = global => { + var check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true;})(global.navigator.userAgent||global.navigator.vendor||global.opera); + return check; +}; +const applyCanvasStylesForMinimalRendering = canvas => { + canvas.style.display = 'block'; + canvas.style.position = 'absolute'; + canvas.style.width = canvas.style.height = '1px'; + canvas.style.top = canvas.style.left = '0px'; +}; + +var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + + +function unwrapExports (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} + +var cardboardVrDisplay = createCommonjsModule(function (module, exports) { +(function (global, factory) { + module.exports = factory(); +}(commonjsGlobal, (function () { var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; +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 slicedToArray = function () { + function sliceIterator(arr, i) { + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"]) _i["return"](); + } finally { + if (_d) throw _e; + } + } + return _arr; + } + return function (arr, i) { + if (Array.isArray(arr)) { + return arr; + } else if (Symbol.iterator in Object(arr)) { + return sliceIterator(arr, i); + } else { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + }; +}(); +var MIN_TIMESTEP = 0.001; +var MAX_TIMESTEP = 1; +var dataUri = function dataUri(mimeType, svg) { + return 'data:' + mimeType + ',' + encodeURIComponent(svg); +}; +var lerp = function lerp(a, b, t) { + return a + (b - a) * t; +}; +var isIOS = function () { + var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); + return function () { + return isIOS; + }; +}(); +var isWebViewAndroid = function () { + var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1; + return function () { + return isWebViewAndroid; + }; +}(); +var isSafari = function () { + var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + return function () { + return isSafari; + }; +}(); +var isFirefoxAndroid = function () { + var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; + return function () { + return isFirefoxAndroid; + }; +}(); +var getChromeVersion = function () { + var match = navigator.userAgent.match(/.*Chrome\/([0-9]+)/); + var value = match ? parseInt(match[1], 10) : null; + return function () { + return value; + }; +}(); +var isChromeWithoutDeviceMotion = function () { + var value = false; + if (getChromeVersion() === 65) { + var match = navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/); + if (match) { + var _match$1$split = match[1].split('.'), + _match$1$split2 = slicedToArray(_match$1$split, 4), + major = _match$1$split2[0], + minor = _match$1$split2[1], + branch = _match$1$split2[2], + build = _match$1$split2[3]; + value = parseInt(branch, 10) === 3325 && parseInt(build, 10) < 148; + } + } + return function () { + return value; + }; +}(); +var isR7 = function () { + var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1; + return function () { + return isR7; + }; +}(); +var isLandscapeMode = function isLandscapeMode() { + var rtn = window.orientation == 90 || window.orientation == -90; + return isR7() ? !rtn : rtn; +}; +var isTimestampDeltaValid = function isTimestampDeltaValid(timestampDeltaS) { + if (isNaN(timestampDeltaS)) { + return false; + } + if (timestampDeltaS <= MIN_TIMESTEP) { + return false; + } + if (timestampDeltaS > MAX_TIMESTEP) { + return false; + } + return true; +}; +var getScreenWidth = function getScreenWidth() { + return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio; +}; +var getScreenHeight = function getScreenHeight() { + return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio; +}; +var requestFullscreen = function requestFullscreen(element) { + if (isWebViewAndroid()) { + return false; + } + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } else { + return false; + } + return true; +}; +var exitFullscreen = function exitFullscreen() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else { + return false; + } + return true; +}; +var getFullscreenElement = function getFullscreenElement() { + return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; +}; +var linkProgram = function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) { + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexSource); + gl.compileShader(vertexShader); + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentSource); + gl.compileShader(fragmentShader); + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + for (var attribName in attribLocationMap) { + gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); + }gl.linkProgram(program); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return program; +}; +var getProgramUniforms = function getProgramUniforms(gl, program) { + var uniforms = {}; + var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + var uniformName = ''; + for (var i = 0; i < uniformCount; i++) { + var uniformInfo = gl.getActiveUniform(program, i); + uniformName = uniformInfo.name.replace('[0]', ''); + uniforms[uniformName] = gl.getUniformLocation(program, uniformName); + } + return uniforms; +}; +var orthoMatrix = function orthoMatrix(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + 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; +}; +var isMobile = function isMobile() { + var check = false; + (function (a) { + if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; + })(navigator.userAgent || navigator.vendor || window.opera); + return check; +}; +var extend = function extend(dest, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dest[key] = src[key]; + } + } + return dest; +}; +var safariCssSizeWorkaround = function safariCssSizeWorkaround(canvas) { + if (isIOS()) { + var width = canvas.style.width; + var height = canvas.style.height; + canvas.style.width = parseInt(width) + 1 + 'px'; + canvas.style.height = parseInt(height) + 'px'; + setTimeout(function () { + canvas.style.width = width; + canvas.style.height = height; + }, 100); + } + window.canvas = canvas; +}; +var frameDataFromPose = function () { + var piOver180 = Math.PI / 180.0; + var rad45 = Math.PI * 0.25; + function mat4_perspectiveFromFieldOfView(out, fov, near, far) { + var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45), + downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45), + leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45), + rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45), + xScale = 2.0 / (leftTan + rightTan), + 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; + } + function mat4_fromRotationTranslation(out, q, v) { + var x = q[0], + y = q[1], + z = q[2], + w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + 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; + } + function mat4_translate(out, a, v) { + var x = v[0], + y = v[1], + z = v[2], + a00, + a01, + a02, + a03, + a10, + a11, + a12, + a13, + 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; + } + function mat4_invert(out, a) { + var 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], + a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15], + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + 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; + } + var defaultOrientation = new Float32Array([0, 0, 0, 1]); + var defaultPosition = new Float32Array([0, 0, 0]); + function updateEyeMatrices(projection, view, pose, fov, offset, vrDisplay) { + mat4_perspectiveFromFieldOfView(projection, fov || null, vrDisplay.depthNear, vrDisplay.depthFar); + var orientation = pose.orientation || defaultOrientation; + var position = pose.position || defaultPosition; + mat4_fromRotationTranslation(view, orientation, position); + if (offset) mat4_translate(view, view, offset); + mat4_invert(view, view); + } + return function (frameData, pose, vrDisplay) { + if (!frameData || !pose) return false; + frameData.pose = pose; + frameData.timestamp = pose.timestamp; + updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay._getFieldOfView("left"), vrDisplay._getEyeOffset("left"), vrDisplay); + updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay._getFieldOfView("right"), vrDisplay._getEyeOffset("right"), vrDisplay); + return true; + }; +}(); +var isInsideCrossOriginIFrame = function isInsideCrossOriginIFrame() { + var isFramed = window.self !== window.top; + var refOrigin = getOriginFromUrl(document.referrer); + var thisOrigin = getOriginFromUrl(window.location.href); + return isFramed && refOrigin !== thisOrigin; +}; +var getOriginFromUrl = function getOriginFromUrl(url) { + var domainIdx; + var protoSepIdx = url.indexOf("://"); + if (protoSepIdx !== -1) { + domainIdx = protoSepIdx + 3; + } else { + domainIdx = 0; + } + var domainEndIdx = url.indexOf('/', domainIdx); + if (domainEndIdx === -1) { + domainEndIdx = url.length; + } + return url.substring(0, domainEndIdx); +}; +var getQuaternionAngle = function getQuaternionAngle(quat) { + if (quat.w > 1) { + console.warn('getQuaternionAngle: w > 1'); + return 0; + } + var angle = 2 * Math.acos(quat.w); + return angle; +}; +var warnOnce = function () { + var observedWarnings = {}; + return function (key, message) { + if (observedWarnings[key] === undefined) { + console.warn('webvr-polyfill: ' + message); + observedWarnings[key] = true; + } + }; +}(); +var deprecateWarning = function deprecateWarning(deprecated, suggested) { + var alternative = suggested ? 'Please use ' + suggested + ' instead.' : ''; + warnOnce(deprecated, deprecated + ' has been deprecated. ' + 'This may not work on native WebVR displays. ' + alternative); +}; +function WGLUPreserveGLState(gl, bindings, callback) { + if (!bindings) { + callback(gl); + return; + } + var boundValues = []; + var activeTexture = null; + for (var i = 0; i < bindings.length; ++i) { + var binding = bindings[i]; + switch (binding) { + case gl.TEXTURE_BINDING_2D: + case gl.TEXTURE_BINDING_CUBE_MAP: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) { + console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"); + boundValues.push(null, null); + break; + } + if (!activeTexture) { + activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); + } + gl.activeTexture(textureUnit); + boundValues.push(gl.getParameter(binding), null); + break; + case gl.ACTIVE_TEXTURE: + activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); + boundValues.push(null); + break; + default: + boundValues.push(gl.getParameter(binding)); + break; + } + } + callback(gl); + for (var i = 0; i < bindings.length; ++i) { + var binding = bindings[i]; + var boundValue = boundValues[i]; + switch (binding) { + case gl.ACTIVE_TEXTURE: + break; + case gl.ARRAY_BUFFER_BINDING: + gl.bindBuffer(gl.ARRAY_BUFFER, boundValue); + break; + case gl.COLOR_CLEAR_VALUE: + gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.COLOR_WRITEMASK: + gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.CURRENT_PROGRAM: + gl.useProgram(boundValue); + break; + case gl.ELEMENT_ARRAY_BUFFER_BINDING: + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue); + break; + case gl.FRAMEBUFFER_BINDING: + gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue); + break; + case gl.RENDERBUFFER_BINDING: + gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue); + break; + case gl.TEXTURE_BINDING_2D: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) + break; + gl.activeTexture(textureUnit); + gl.bindTexture(gl.TEXTURE_2D, boundValue); + break; + case gl.TEXTURE_BINDING_CUBE_MAP: + var textureUnit = bindings[++i]; + if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) + break; + gl.activeTexture(textureUnit); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue); + break; + case gl.VIEWPORT: + gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); + break; + case gl.BLEND: + case gl.CULL_FACE: + case gl.DEPTH_TEST: + case gl.SCISSOR_TEST: + case gl.STENCIL_TEST: + if (boundValue) { + gl.enable(binding); + } else { + gl.disable(binding); + } + break; + default: + console.log("No GL restore behavior for 0x" + binding.toString(16)); + break; + } + if (activeTexture) { + gl.activeTexture(activeTexture); + } + } +} +var glPreserveState = WGLUPreserveGLState; +var distortionVS = ['attribute vec2 position;', 'attribute vec3 texCoord;', 'varying vec2 vTexCoord;', 'uniform vec4 viewportOffsetScale[2];', 'void main() {', ' vec4 viewport = viewportOffsetScale[int(texCoord.z)];', ' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', ' gl_Position = vec4( position, 1.0, 1.0 );', '}'].join('\n'); +var distortionFS = ['precision mediump float;', 'uniform sampler2D diffuse;', 'varying vec2 vTexCoord;', 'void main() {', ' gl_FragColor = texture2D(diffuse, vTexCoord);', '}'].join('\n'); +function CardboardDistorter(gl, cardboardUI, bufferScale, dirtySubmitFrameBindings) { + this.gl = gl; + this.cardboardUI = cardboardUI; + this.bufferScale = bufferScale; + this.dirtySubmitFrameBindings = dirtySubmitFrameBindings; + this.ctxAttribs = gl.getContextAttributes(); + this.meshWidth = 20; + this.meshHeight = 20; + this.bufferWidth = gl.drawingBufferWidth; + this.bufferHeight = gl.drawingBufferHeight; + this.realBindFramebuffer = gl.bindFramebuffer; + this.realEnable = gl.enable; + this.realDisable = gl.disable; + this.realColorMask = gl.colorMask; + this.realClearColor = gl.clearColor; + this.realViewport = gl.viewport; + if (!isIOS()) { + this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width'); + this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height'); + } + this.isPatched = false; + this.lastBoundFramebuffer = null; + this.cullFace = false; + this.depthTest = false; + this.blend = false; + this.scissorTest = false; + this.stencilTest = false; + this.viewport = [0, 0, 0, 0]; + this.colorMask = [true, true, true, true]; + this.clearColor = [0, 0, 0, 0]; + this.attribs = { + position: 0, + texCoord: 1 + }; + this.program = linkProgram(gl, distortionVS, distortionFS, this.attribs); + this.uniforms = getProgramUniforms(gl, this.program); + this.viewportOffsetScale = new Float32Array(8); + this.setTextureBounds(); + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + this.indexCount = 0; + this.renderTarget = gl.createTexture(); + this.framebuffer = gl.createFramebuffer(); + this.depthStencilBuffer = null; + this.depthBuffer = null; + this.stencilBuffer = null; + if (this.ctxAttribs.depth && this.ctxAttribs.stencil) { + this.depthStencilBuffer = gl.createRenderbuffer(); + } else if (this.ctxAttribs.depth) { + this.depthBuffer = gl.createRenderbuffer(); + } else if (this.ctxAttribs.stencil) { + this.stencilBuffer = gl.createRenderbuffer(); + } + this.patch(); + this.onResize(); +} +CardboardDistorter.prototype.destroy = function () { + var gl = this.gl; + this.unpatch(); + gl.deleteProgram(this.program); + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.indexBuffer); + gl.deleteTexture(this.renderTarget); + gl.deleteFramebuffer(this.framebuffer); + if (this.depthStencilBuffer) { + gl.deleteRenderbuffer(this.depthStencilBuffer); + } + if (this.depthBuffer) { + gl.deleteRenderbuffer(this.depthBuffer); + } + if (this.stencilBuffer) { + gl.deleteRenderbuffer(this.stencilBuffer); + } + if (this.cardboardUI) { + this.cardboardUI.destroy(); + } +}; +CardboardDistorter.prototype.onResize = function () { + var gl = this.gl; + var self = this; + var glState = [gl.RENDERBUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0]; + glPreserveState(gl, glState, function (gl) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); + if (self.scissorTest) { + self.realDisable.call(gl, gl.SCISSOR_TEST); + } + self.realColorMask.call(gl, true, true, true, true); + self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + self.realClearColor.call(gl, 0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer); + gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); + gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, self.bufferWidth, self.bufferHeight, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0); + if (self.ctxAttribs.depth && self.ctxAttribs.stencil) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.depthStencilBuffer); + } else if (self.ctxAttribs.depth) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, self.depthBuffer); + } else if (self.ctxAttribs.stencil) { + gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, self.bufferWidth, self.bufferHeight); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.stencilBuffer); + } + if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) { + console.error('Framebuffer incomplete!'); + } + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); + if (self.scissorTest) { + self.realEnable.call(gl, gl.SCISSOR_TEST); + } + self.realColorMask.apply(gl, self.colorMask); + self.realViewport.apply(gl, self.viewport); + self.realClearColor.apply(gl, self.clearColor); + }); + if (this.cardboardUI) { + this.cardboardUI.onResize(); + } +}; +CardboardDistorter.prototype.patch = function () { + if (this.isPatched) { + return; + } + var self = this; + var canvas = this.gl.canvas; + var gl = this.gl; + if (!isIOS()) { + canvas.width = getScreenWidth() * this.bufferScale; + canvas.height = getScreenHeight() * this.bufferScale; + Object.defineProperty(canvas, 'width', { + configurable: true, + enumerable: true, + get: function get() { + return self.bufferWidth; + }, + set: function set(value) { + self.bufferWidth = value; + self.realCanvasWidth.set.call(canvas, value); + self.onResize(); + } + }); + Object.defineProperty(canvas, 'height', { + configurable: true, + enumerable: true, + get: function get() { + return self.bufferHeight; + }, + set: function set(value) { + self.bufferHeight = value; + self.realCanvasHeight.set.call(canvas, value); + self.onResize(); + } + }); + } + this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); + if (this.lastBoundFramebuffer == null) { + this.lastBoundFramebuffer = this.framebuffer; + this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); + } + this.gl.bindFramebuffer = function (target, framebuffer) { + self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer; + self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer); + }; + this.cullFace = gl.getParameter(gl.CULL_FACE); + this.depthTest = gl.getParameter(gl.DEPTH_TEST); + this.blend = gl.getParameter(gl.BLEND); + this.scissorTest = gl.getParameter(gl.SCISSOR_TEST); + this.stencilTest = gl.getParameter(gl.STENCIL_TEST); + gl.enable = function (pname) { + switch (pname) { + case gl.CULL_FACE: + self.cullFace = true;break; + case gl.DEPTH_TEST: + self.depthTest = true;break; + case gl.BLEND: + self.blend = true;break; + case gl.SCISSOR_TEST: + self.scissorTest = true;break; + case gl.STENCIL_TEST: + self.stencilTest = true;break; + } + self.realEnable.call(gl, pname); + }; + gl.disable = function (pname) { + switch (pname) { + case gl.CULL_FACE: + self.cullFace = false;break; + case gl.DEPTH_TEST: + self.depthTest = false;break; + case gl.BLEND: + self.blend = false;break; + case gl.SCISSOR_TEST: + self.scissorTest = false;break; + case gl.STENCIL_TEST: + self.stencilTest = false;break; + } + self.realDisable.call(gl, pname); + }; + this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK); + gl.colorMask = function (r, g, b, a) { + self.colorMask[0] = r; + self.colorMask[1] = g; + self.colorMask[2] = b; + self.colorMask[3] = a; + self.realColorMask.call(gl, r, g, b, a); + }; + this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); + gl.clearColor = function (r, g, b, a) { + self.clearColor[0] = r; + self.clearColor[1] = g; + self.clearColor[2] = b; + self.clearColor[3] = a; + self.realClearColor.call(gl, r, g, b, a); + }; + this.viewport = gl.getParameter(gl.VIEWPORT); + gl.viewport = function (x, y, w, h) { + self.viewport[0] = x; + self.viewport[1] = y; + self.viewport[2] = w; + self.viewport[3] = h; + self.realViewport.call(gl, x, y, w, h); + }; + this.isPatched = true; + safariCssSizeWorkaround(canvas); +}; +CardboardDistorter.prototype.unpatch = function () { + if (!this.isPatched) { + return; + } + var gl = this.gl; + var canvas = this.gl.canvas; + if (!isIOS()) { + Object.defineProperty(canvas, 'width', this.realCanvasWidth); + Object.defineProperty(canvas, 'height', this.realCanvasHeight); + } + canvas.width = this.bufferWidth; + canvas.height = this.bufferHeight; + gl.bindFramebuffer = this.realBindFramebuffer; + gl.enable = this.realEnable; + gl.disable = this.realDisable; + gl.colorMask = this.realColorMask; + gl.clearColor = this.realClearColor; + gl.viewport = this.realViewport; + if (this.lastBoundFramebuffer == this.framebuffer) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + this.isPatched = false; + setTimeout(function () { + safariCssSizeWorkaround(canvas); + }, 1); +}; +CardboardDistorter.prototype.setTextureBounds = function (leftBounds, rightBounds) { + if (!leftBounds) { + leftBounds = [0, 0, 0.5, 1]; + } + if (!rightBounds) { + rightBounds = [0.5, 0, 0.5, 1]; + } + this.viewportOffsetScale[0] = leftBounds[0]; + this.viewportOffsetScale[1] = leftBounds[1]; + this.viewportOffsetScale[2] = leftBounds[2]; + this.viewportOffsetScale[3] = leftBounds[3]; + this.viewportOffsetScale[4] = rightBounds[0]; + this.viewportOffsetScale[5] = rightBounds[1]; + this.viewportOffsetScale[6] = rightBounds[2]; + this.viewportOffsetScale[7] = rightBounds[3]; +}; +CardboardDistorter.prototype.submitFrame = function () { + var gl = this.gl; + var self = this; + var glState = []; + if (!this.dirtySubmitFrameBindings) { + glState.push(gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0); + } + glPreserveState(gl, glState, function (gl) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); + if (self.cullFace) { + self.realDisable.call(gl, gl.CULL_FACE); + } + if (self.depthTest) { + self.realDisable.call(gl, gl.DEPTH_TEST); + } + if (self.blend) { + self.realDisable.call(gl, gl.BLEND); + } + if (self.scissorTest) { + self.realDisable.call(gl, gl.SCISSOR_TEST); + } + if (self.stencilTest) { + self.realDisable.call(gl, gl.STENCIL_TEST); + } + self.realColorMask.call(gl, true, true, true, true); + self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + if (self.ctxAttribs.alpha || isIOS()) { + self.realClearColor.call(gl, 0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + } + gl.useProgram(self.program); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.enableVertexAttribArray(self.attribs.position); + gl.enableVertexAttribArray(self.attribs.texCoord); + gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0); + gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8); + gl.activeTexture(gl.TEXTURE0); + gl.uniform1i(self.uniforms.diffuse, 0); + gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); + gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale); + gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0); + if (self.cardboardUI) { + self.cardboardUI.renderNoState(); + } + self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer); + if (!self.ctxAttribs.preserveDrawingBuffer) { + self.realClearColor.call(gl, 0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + if (!self.dirtySubmitFrameBindings) { + self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); + } + if (self.cullFace) { + self.realEnable.call(gl, gl.CULL_FACE); + } + if (self.depthTest) { + self.realEnable.call(gl, gl.DEPTH_TEST); + } + if (self.blend) { + self.realEnable.call(gl, gl.BLEND); + } + if (self.scissorTest) { + self.realEnable.call(gl, gl.SCISSOR_TEST); + } + if (self.stencilTest) { + self.realEnable.call(gl, gl.STENCIL_TEST); + } + self.realColorMask.apply(gl, self.colorMask); + self.realViewport.apply(gl, self.viewport); + if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) { + self.realClearColor.apply(gl, self.clearColor); + } + }); + if (isIOS()) { + var canvas = gl.canvas; + if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) { + self.bufferWidth = canvas.width; + self.bufferHeight = canvas.height; + self.onResize(); + } + } +}; +CardboardDistorter.prototype.updateDeviceInfo = function (deviceInfo) { + var gl = this.gl; + var self = this; + var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo); + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + if (!self.indexCount) { + var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + self.indexCount = indices.length; + } + }); +}; +CardboardDistorter.prototype.computeMeshVertices_ = function (width, height, deviceInfo) { + var vertices = new Float32Array(2 * width * height * 5); + var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); + var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); + var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); + var vidx = 0; + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + var u = i / (width - 1); + var v = j / (height - 1); + var s = u; + var t = v; + var x = lerp(lensFrustum[0], lensFrustum[2], u); + var y = lerp(lensFrustum[3], lensFrustum[1], v); + var d = Math.sqrt(x * x + y * y); + var r = deviceInfo.distortion.distortInverse(d); + var p = x * r / d; + var q = y * r / d; + u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); + v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); + u = (viewport.x + u * viewport.width - 0.5) * 2.0; + v = (viewport.y + v * viewport.height - 0.5) * 2.0; + vertices[vidx * 5 + 0] = u; + vertices[vidx * 5 + 1] = v; + vertices[vidx * 5 + 2] = s; + vertices[vidx * 5 + 3] = t; + vertices[vidx * 5 + 4] = e; + } + } + var w = lensFrustum[2] - lensFrustum[0]; + lensFrustum[0] = -(w + lensFrustum[0]); + lensFrustum[2] = w - lensFrustum[2]; + w = noLensFrustum[2] - noLensFrustum[0]; + noLensFrustum[0] = -(w + noLensFrustum[0]); + noLensFrustum[2] = w - noLensFrustum[2]; + viewport.x = 1 - (viewport.x + viewport.width); + } + return vertices; +}; +CardboardDistorter.prototype.computeMeshIndices_ = function (width, height) { + var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); + var halfwidth = width / 2; + var halfheight = height / 2; + var vidx = 0; + var iidx = 0; + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + if (i == 0 || j == 0) continue; + if (i <= halfwidth == j <= halfheight) { + indices[iidx++] = vidx; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx; + indices[iidx++] = vidx - 1; + } else { + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width - 1; + } + } + } + } + return indices; +}; +CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function (proto, attrName) { + var descriptor = Object.getOwnPropertyDescriptor(proto, attrName); + if (descriptor.get === undefined || descriptor.set === undefined) { + descriptor.configurable = true; + descriptor.enumerable = true; + descriptor.get = function () { + return this.getAttribute(attrName); + }; + descriptor.set = function (val) { + this.setAttribute(attrName, val); + }; + } + return descriptor; +}; +var uiVS = ['attribute vec2 position;', 'uniform mat4 projectionMat;', 'void main() {', ' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', '}'].join('\n'); +var uiFS = ['precision mediump float;', 'uniform vec4 color;', 'void main() {', ' gl_FragColor = color;', '}'].join('\n'); +var DEG2RAD = Math.PI / 180.0; +var kAnglePerGearSection = 60; +var kOuterRimEndAngle = 12; +var kInnerRimBeginAngle = 20; +var kOuterRadius = 1; +var kMiddleRadius = 0.75; +var kInnerRadius = 0.3125; +var kCenterLineThicknessDp = 4; +var kButtonWidthDp = 28; +var kTouchSlopFactor = 1.5; +function CardboardUI(gl) { + this.gl = gl; + this.attribs = { + position: 0 + }; + this.program = linkProgram(gl, uiVS, uiFS, this.attribs); + this.uniforms = getProgramUniforms(gl, this.program); + this.vertexBuffer = gl.createBuffer(); + this.gearOffset = 0; + this.gearVertexCount = 0; + this.arrowOffset = 0; + this.arrowVertexCount = 0; + this.projMat = new Float32Array(16); + this.listener = null; + this.onResize(); +} +CardboardUI.prototype.destroy = function () { + var gl = this.gl; + if (this.listener) { + gl.canvas.removeEventListener('click', this.listener, false); + } + gl.deleteProgram(this.program); + gl.deleteBuffer(this.vertexBuffer); +}; +CardboardUI.prototype.listen = function (optionsCallback, backCallback) { + var canvas = this.gl.canvas; + this.listener = function (event) { + var midline = canvas.clientWidth / 2; + var buttonSize = kButtonWidthDp * kTouchSlopFactor; + if (event.clientX > midline - buttonSize && event.clientX < midline + buttonSize && event.clientY > canvas.clientHeight - buttonSize) { + optionsCallback(event); + } + else if (event.clientX < buttonSize && event.clientY < buttonSize) { + backCallback(event); + } + }; + canvas.addEventListener('click', this.listener, false); +}; +CardboardUI.prototype.onResize = function () { + var gl = this.gl; + var self = this; + var glState = [gl.ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + var vertices = []; + var midline = gl.drawingBufferWidth / 2; + var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio; + var scalingRatio = gl.drawingBufferWidth / physicalPixels; + var dps = scalingRatio * window.devicePixelRatio; + var lineWidth = kCenterLineThicknessDp * dps / 2; + var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps; + var buttonScale = kButtonWidthDp * dps / 2; + var buttonBorder = (kButtonWidthDp * kTouchSlopFactor - kButtonWidthDp) * dps; + vertices.push(midline - lineWidth, buttonSize); + vertices.push(midline - lineWidth, gl.drawingBufferHeight); + vertices.push(midline + lineWidth, buttonSize); + vertices.push(midline + lineWidth, gl.drawingBufferHeight); + self.gearOffset = vertices.length / 2; + function addGearSegment(theta, r) { + var angle = (90 - theta) * DEG2RAD; + var x = Math.cos(angle); + var y = Math.sin(angle); + vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale); + vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale); + } + for (var i = 0; i <= 6; i++) { + var segmentTheta = i * kAnglePerGearSection; + addGearSegment(segmentTheta, kOuterRadius); + addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius); + addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius); + addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius); + addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius); + } + self.gearVertexCount = vertices.length / 2 - self.gearOffset; + self.arrowOffset = vertices.length / 2; + function addArrowVertex(x, y) { + vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y); + } + var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD); + addArrowVertex(0, buttonScale); + addArrowVertex(buttonScale, 0); + addArrowVertex(buttonScale + angledLineWidth, angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale + angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); + addArrowVertex(0, buttonScale); + addArrowVertex(buttonScale, buttonScale * 2); + addArrowVertex(buttonScale + angledLineWidth, buttonScale * 2 - angledLineWidth); + addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); + addArrowVertex(0, buttonScale); + addArrowVertex(angledLineWidth, buttonScale - lineWidth); + addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth); + addArrowVertex(angledLineWidth, buttonScale + lineWidth); + addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth); + self.arrowVertexCount = vertices.length / 2 - self.arrowOffset; + gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + }); +}; +CardboardUI.prototype.render = function () { + var gl = this.gl; + var self = this; + var glState = [gl.CULL_FACE, gl.DEPTH_TEST, gl.BLEND, gl.SCISSOR_TEST, gl.STENCIL_TEST, gl.COLOR_WRITEMASK, gl.VIEWPORT, gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING]; + glPreserveState(gl, glState, function (gl) { + gl.disable(gl.CULL_FACE); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + gl.disable(gl.SCISSOR_TEST); + gl.disable(gl.STENCIL_TEST); + gl.colorMask(true, true, true, true); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + self.renderNoState(); + }); +}; +CardboardUI.prototype.renderNoState = function () { + var gl = this.gl; + gl.useProgram(this.program); + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.enableVertexAttribArray(this.attribs.position); + gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0); + gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0); + orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0); + gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount); + gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount); +}; +function Distortion(coefficients) { + this.coefficients = coefficients; +} +Distortion.prototype.distortInverse = function (radius) { + var r0 = 0; + var r1 = 1; + var dr0 = radius - this.distort(r0); + while (Math.abs(r1 - r0) > 0.0001 ) { + var dr1 = radius - this.distort(r1); + var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); + r0 = r1; + r1 = r2; + dr0 = dr1; + } + return r1; +}; +Distortion.prototype.distort = function (radius) { + var r2 = radius * radius; + var ret = 0; + for (var i = 0; i < this.coefficients.length; i++) { + ret = r2 * (ret + this.coefficients[i]); + } + return (ret + 1) * radius; +}; +var degToRad = Math.PI / 180; +var radToDeg = 180 / Math.PI; +var Vector3 = function Vector3(x, y, z) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; +}; +Vector3.prototype = { + constructor: Vector3, + set: function set(x, y, z) { + this.x = x; + this.y = y; + this.z = z; + return this; + }, + copy: function copy(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + return this; + }, + length: function length() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); + }, + normalize: function normalize() { + var scalar = this.length(); + if (scalar !== 0) { + var invScalar = 1 / scalar; + this.multiplyScalar(invScalar); + } else { + this.x = 0; + this.y = 0; + this.z = 0; + } + return this; + }, + multiplyScalar: function multiplyScalar(scalar) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + }, + applyQuaternion: function applyQuaternion(q) { + var x = this.x; + var y = this.y; + var z = this.z; + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = -qx * x - qy * y - qz * z; + this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return this; + }, + dot: function dot(v) { + return this.x * v.x + this.y * v.y + this.z * v.z; + }, + crossVectors: function crossVectors(a, b) { + var ax = a.x, + ay = a.y, + az = a.z; + var bx = b.x, + by = b.y, + bz = b.z; + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + return this; + } +}; +var Quaternion = function Quaternion(x, y, z, w) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = w !== undefined ? w : 1; +}; +Quaternion.prototype = { + constructor: Quaternion, + set: function set(x, y, z, w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + }, + copy: function copy(quaternion) { + this.x = quaternion.x; + this.y = quaternion.y; + this.z = quaternion.z; + this.w = quaternion.w; + return this; + }, + setFromEulerXYZ: function setFromEulerXYZ(x, y, z) { + var c1 = Math.cos(x / 2); + var c2 = Math.cos(y / 2); + var c3 = Math.cos(z / 2); + var s1 = Math.sin(x / 2); + var s2 = Math.sin(y / 2); + var s3 = Math.sin(z / 2); + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + return this; + }, + setFromEulerYXZ: function setFromEulerYXZ(x, y, z) { + var c1 = Math.cos(x / 2); + var c2 = Math.cos(y / 2); + var c3 = Math.cos(z / 2); + var s1 = Math.sin(x / 2); + var s2 = Math.sin(y / 2); + var s3 = Math.sin(z / 2); + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + return this; + }, + setFromAxisAngle: function setFromAxisAngle(axis, angle) { + var halfAngle = angle / 2, + s = Math.sin(halfAngle); + this.x = axis.x * s; + this.y = axis.y * s; + this.z = axis.z * s; + this.w = Math.cos(halfAngle); + return this; + }, + multiply: function multiply(q) { + return this.multiplyQuaternions(this, q); + }, + multiplyQuaternions: function multiplyQuaternions(a, b) { + var qax = a.x, + qay = a.y, + qaz = a.z, + qaw = a.w; + var qbx = b.x, + qby = b.y, + qbz = b.z, + qbw = b.w; + this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + return this; + }, + inverse: function inverse() { + this.x *= -1; + this.y *= -1; + this.z *= -1; + this.normalize(); + return this; + }, + normalize: function normalize() { + var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + if (l === 0) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + } else { + l = 1 / l; + this.x = this.x * l; + this.y = this.y * l; + this.z = this.z * l; + this.w = this.w * l; + } + return this; + }, + slerp: function slerp(qb, t) { + if (t === 0) return this; + if (t === 1) return this.copy(qb); + var x = this.x, + y = this.y, + z = this.z, + w = this.w; + var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; + if (cosHalfTheta < 0) { + this.w = -qb.w; + this.x = -qb.x; + this.y = -qb.y; + this.z = -qb.z; + cosHalfTheta = -cosHalfTheta; + } else { + this.copy(qb); + } + if (cosHalfTheta >= 1.0) { + this.w = w; + this.x = x; + this.y = y; + this.z = z; + return this; + } + var halfTheta = Math.acos(cosHalfTheta); + var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); + if (Math.abs(sinHalfTheta) < 0.001) { + this.w = 0.5 * (w + this.w); + this.x = 0.5 * (x + this.x); + this.y = 0.5 * (y + this.y); + this.z = 0.5 * (z + this.z); + return this; + } + var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + this.w = w * ratioA + this.w * ratioB; + this.x = x * ratioA + this.x * ratioB; + this.y = y * ratioA + this.y * ratioB; + this.z = z * ratioA + this.z * ratioB; + return this; + }, + setFromUnitVectors: function () { + var v1, r; + var EPS = 0.000001; + return function (vFrom, vTo) { + if (v1 === undefined) v1 = new Vector3(); + r = vFrom.dot(vTo) + 1; + if (r < EPS) { + r = 0; + if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { + v1.set(-vFrom.y, vFrom.x, 0); + } else { + v1.set(0, -vFrom.z, vFrom.y); + } + } else { + v1.crossVectors(vFrom, vTo); + } + this.x = v1.x; + this.y = v1.y; + this.z = v1.z; + this.w = r; + this.normalize(); + return this; + }; + }() +}; +function Device(params) { + this.width = params.width || getScreenWidth(); + this.height = params.height || getScreenHeight(); + this.widthMeters = params.widthMeters; + this.heightMeters = params.heightMeters; + this.bevelMeters = params.bevelMeters; +} +var DEFAULT_ANDROID = new Device({ + widthMeters: 0.110, + heightMeters: 0.062, + bevelMeters: 0.004 +}); +var DEFAULT_IOS = new Device({ + widthMeters: 0.1038, + heightMeters: 0.0584, + bevelMeters: 0.004 +}); +var Viewers = { + CardboardV1: new CardboardViewer({ + id: 'CardboardV1', + label: 'Cardboard I/O 2014', + fov: 40, + interLensDistance: 0.060, + baselineLensDistance: 0.035, + screenLensDistance: 0.042, + distortionCoefficients: [0.441, 0.156], + inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, 0.0651772, -0.01488963, 0.001559834] + }), + CardboardV2: new CardboardViewer({ + id: 'CardboardV2', + label: 'Cardboard I/O 2015', + fov: 60, + interLensDistance: 0.064, + baselineLensDistance: 0.035, + screenLensDistance: 0.039, + distortionCoefficients: [0.34, 0.55], + inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, -9.904169E-4, 6.183535E-5, -1.6981803E-6] + }) +}; +function DeviceInfo(deviceParams, additionalViewers) { + this.viewer = Viewers.CardboardV2; + this.updateDeviceParams(deviceParams); + this.distortion = new Distortion(this.viewer.distortionCoefficients); + for (var i = 0; i < additionalViewers.length; i++) { + var viewer = additionalViewers[i]; + Viewers[viewer.id] = new CardboardViewer(viewer); + } +} +DeviceInfo.prototype.updateDeviceParams = function (deviceParams) { + this.device = this.determineDevice_(deviceParams) || this.device; +}; +DeviceInfo.prototype.getDevice = function () { + return this.device; +}; +DeviceInfo.prototype.setViewer = function (viewer) { + this.viewer = viewer; + this.distortion = new Distortion(this.viewer.distortionCoefficients); +}; +DeviceInfo.prototype.determineDevice_ = function (deviceParams) { + if (!deviceParams) { + if (isIOS()) { + console.warn('Using fallback iOS device measurements.'); + return DEFAULT_IOS; + } else { + console.warn('Using fallback Android device measurements.'); + return DEFAULT_ANDROID; + } + } + var METERS_PER_INCH = 0.0254; + var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi; + var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi; + var width = getScreenWidth(); + var height = getScreenHeight(); + return new Device({ + widthMeters: metersPerPixelX * width, + heightMeters: metersPerPixelY * height, + bevelMeters: deviceParams.bevelMm * 0.001 + }); +}; +DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var eyeToScreenDistance = viewer.screenLensDistance; + var outerDist = (device.widthMeters - viewer.interLensDistance) / 2; + var innerDist = viewer.interLensDistance / 2; + var bottomDist = viewer.baselineLensDistance - device.bevelMeters; + var topDist = device.heightMeters - bottomDist; + var outerAngle = radToDeg * Math.atan(distortion.distort(outerDist / eyeToScreenDistance)); + var innerAngle = radToDeg * Math.atan(distortion.distort(innerDist / eyeToScreenDistance)); + var bottomAngle = radToDeg * Math.atan(distortion.distort(bottomDist / eyeToScreenDistance)); + var topAngle = radToDeg * Math.atan(distortion.distort(topDist / eyeToScreenDistance)); + return { + leftDegrees: Math.min(outerAngle, viewer.fov), + rightDegrees: Math.min(innerAngle, viewer.fov), + downDegrees: Math.min(bottomAngle, viewer.fov), + upDegrees: Math.min(topAngle, viewer.fov) + }; +}; +DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var fovLeft = Math.tan(-degToRad * viewer.fov); + var fovTop = Math.tan(degToRad * viewer.fov); + var fovRight = Math.tan(degToRad * viewer.fov); + var fovBottom = Math.tan(-degToRad * viewer.fov); + var halfWidth = device.widthMeters / 4; + var halfHeight = device.heightMeters / 2; + var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; + var centerX = viewer.interLensDistance / 2 - halfWidth; + var centerY = -verticalLensOffset; + var centerZ = viewer.screenLensDistance; + var screenLeft = distortion.distort((centerX - halfWidth) / centerZ); + var screenTop = distortion.distort((centerY + halfHeight) / centerZ); + var screenRight = distortion.distort((centerX + halfWidth) / centerZ); + var screenBottom = distortion.distort((centerY - halfHeight) / centerZ); + var result = new Float32Array(4); + result[0] = Math.max(fovLeft, screenLeft); + result[1] = Math.min(fovTop, screenTop); + result[2] = Math.min(fovRight, screenRight); + result[3] = Math.max(fovBottom, screenBottom); + return result; +}; +DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var result = new Float32Array(4); + var fovLeft = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); + var fovTop = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); + var fovRight = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); + var fovBottom = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); + var halfWidth = device.widthMeters / 4; + var halfHeight = device.heightMeters / 2; + var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; + var centerX = viewer.interLensDistance / 2 - halfWidth; + var centerY = -verticalLensOffset; + var centerZ = viewer.screenLensDistance; + var screenLeft = (centerX - halfWidth) / centerZ; + var screenTop = (centerY + halfHeight) / centerZ; + var screenRight = (centerX + halfWidth) / centerZ; + var screenBottom = (centerY - halfHeight) / centerZ; + result[0] = Math.max(fovLeft, screenLeft); + result[1] = Math.min(fovTop, screenTop); + result[2] = Math.min(fovRight, screenRight); + result[3] = Math.max(fovBottom, screenBottom); + return result; +}; +DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function (undistortedFrustum) { + var viewer = this.viewer; + var device = this.device; + var dist = viewer.screenLensDistance; + var eyeX = (device.widthMeters - viewer.interLensDistance) / 2; + var eyeY = viewer.baselineLensDistance - device.bevelMeters; + var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters; + var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters; + var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters; + var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters; + return { + x: left, + y: bottom, + width: right - left, + height: top - bottom + }; +}; +DeviceInfo.prototype.getFieldOfViewLeftEye = function (opt_isUndistorted) { + return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : this.getDistortedFieldOfViewLeftEye(); +}; +DeviceInfo.prototype.getFieldOfViewRightEye = function (opt_isUndistorted) { + var fov = this.getFieldOfViewLeftEye(opt_isUndistorted); + return { + leftDegrees: fov.rightDegrees, + rightDegrees: fov.leftDegrees, + upDegrees: fov.upDegrees, + downDegrees: fov.downDegrees + }; +}; +DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function () { + var p = this.getUndistortedParams_(); + return { + leftDegrees: radToDeg * Math.atan(p.outerDist), + rightDegrees: radToDeg * Math.atan(p.innerDist), + downDegrees: radToDeg * Math.atan(p.bottomDist), + upDegrees: radToDeg * Math.atan(p.topDist) + }; +}; +DeviceInfo.prototype.getUndistortedViewportLeftEye = function () { + var p = this.getUndistortedParams_(); + var viewer = this.viewer; + var device = this.device; + var eyeToScreenDistance = viewer.screenLensDistance; + var screenWidth = device.widthMeters / eyeToScreenDistance; + var screenHeight = device.heightMeters / eyeToScreenDistance; + var xPxPerTanAngle = device.width / screenWidth; + var yPxPerTanAngle = device.height / screenHeight; + var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle); + var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle); + return { + x: x, + y: y, + width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x, + height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y + }; +}; +DeviceInfo.prototype.getUndistortedParams_ = function () { + var viewer = this.viewer; + var device = this.device; + var distortion = this.distortion; + var eyeToScreenDistance = viewer.screenLensDistance; + var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance; + var screenWidth = device.widthMeters / eyeToScreenDistance; + var screenHeight = device.heightMeters / eyeToScreenDistance; + var eyePosX = screenWidth / 2 - halfLensDistance; + var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance; + var maxFov = viewer.fov; + var viewerMax = distortion.distortInverse(Math.tan(degToRad * maxFov)); + var outerDist = Math.min(eyePosX, viewerMax); + var innerDist = Math.min(halfLensDistance, viewerMax); + var bottomDist = Math.min(eyePosY, viewerMax); + var topDist = Math.min(screenHeight - eyePosY, viewerMax); + return { + outerDist: outerDist, + innerDist: innerDist, + topDist: topDist, + bottomDist: bottomDist, + eyePosX: eyePosX, + eyePosY: eyePosY + }; +}; +function CardboardViewer(params) { + this.id = params.id; + this.label = params.label; + this.fov = params.fov; + this.interLensDistance = params.interLensDistance; + this.baselineLensDistance = params.baselineLensDistance; + this.screenLensDistance = params.screenLensDistance; + this.distortionCoefficients = params.distortionCoefficients; + this.inverseCoefficients = params.inverseCoefficients; +} +DeviceInfo.Viewers = Viewers; +var format = 1; +var last_updated = "2018-12-10T17:01:42Z"; +var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2 XL/*"},{"ua":"Pixel 2 XL"}],"dpi":537.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3 XL/*"},{"ua":"Pixel 3 XL"}],"dpi":[558.5,553.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 3/*"},{"ua":"Pixel 3"}],"dpi":442.4,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel 2/*"},{"ua":"Pixel 2"}],"dpi":441,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/BLN-L24/*"},{"ua":"HONORBLN-L24"}],"dpi":480,"bw":4,"ac":500},{"type":"android","rules":[{"mdmh":"Huawei/*/BKL-L09/*"},{"ua":"BKL-L09"}],"dpi":403,"bw":3.47,"ac":500},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955F/*"},{"ua":"SM-G955F"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}]; +var DPDB_CACHE = { + format: format, + last_updated: last_updated, + devices: devices +}; +function Dpdb(url, onDeviceParamsUpdated) { + this.dpdb = DPDB_CACHE; + this.recalculateDeviceParams_(); + if (url) { + this.onDeviceParamsUpdated = onDeviceParamsUpdated; + var xhr = new XMLHttpRequest(); + var obj = this; + xhr.open('GET', url, true); + xhr.addEventListener('load', function () { + obj.loading = false; + if (xhr.status >= 200 && xhr.status <= 299) { + obj.dpdb = JSON.parse(xhr.response); + obj.recalculateDeviceParams_(); + } else { + console.error('Error loading online DPDB!'); + } + }); + xhr.send(); + } +} +Dpdb.prototype.getDeviceParams = function () { + return this.deviceParams; +}; +Dpdb.prototype.recalculateDeviceParams_ = function () { + var newDeviceParams = this.calcDeviceParams_(); + if (newDeviceParams) { + this.deviceParams = newDeviceParams; + if (this.onDeviceParamsUpdated) { + this.onDeviceParamsUpdated(this.deviceParams); + } + } else { + console.error('Failed to recalculate device parameters.'); + } +}; +Dpdb.prototype.calcDeviceParams_ = function () { + var db = this.dpdb; + if (!db) { + console.error('DPDB not available.'); + return null; + } + if (db.format != 1) { + console.error('DPDB has unexpected format version.'); + return null; + } + if (!db.devices || !db.devices.length) { + console.error('DPDB does not have a devices section.'); + return null; + } + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + var width = getScreenWidth(); + var height = getScreenHeight(); + if (!db.devices) { + console.error('DPDB has no devices section.'); + return null; + } + for (var i = 0; i < db.devices.length; i++) { + var device = db.devices[i]; + if (!device.rules) { + console.warn('Device[' + i + '] has no rules section.'); + continue; + } + if (device.type != 'ios' && device.type != 'android') { + console.warn('Device[' + i + '] has invalid type.'); + continue; + } + if (isIOS() != (device.type == 'ios')) continue; + var matched = false; + for (var j = 0; j < device.rules.length; j++) { + var rule = device.rules[j]; + if (this.ruleMatches_(rule, userAgent, width, height)) { + matched = true; + break; + } + } + if (!matched) continue; + var xdpi = device.dpi[0] || device.dpi; + var ydpi = device.dpi[1] || device.dpi; + return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw }); + } + console.warn('No DPDB device match.'); + return null; +}; +Dpdb.prototype.ruleMatches_ = function (rule, ua, screenWidth, screenHeight) { + if (!rule.ua && !rule.res) return false; + if (rule.ua && rule.ua.substring(0, 2) === 'SM') rule.ua = rule.ua.substring(0, 7); + if (rule.ua && ua.indexOf(rule.ua) < 0) return false; + if (rule.res) { + if (!rule.res[0] || !rule.res[1]) return false; + var resX = rule.res[0]; + var resY = rule.res[1]; + if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || Math.max(screenWidth, screenHeight) != Math.max(resX, resY)) { + return false; + } + } + return true; +}; +function DeviceParams(params) { + this.xdpi = params.xdpi; + this.ydpi = params.ydpi; + this.bevelMm = params.bevelMm; +} +function SensorSample(sample, timestampS) { + this.set(sample, timestampS); +} +SensorSample.prototype.set = function (sample, timestampS) { + this.sample = sample; + this.timestampS = timestampS; +}; +SensorSample.prototype.copy = function (sensorSample) { + this.set(sensorSample.sample, sensorSample.timestampS); +}; +function ComplementaryFilter(kFilter, isDebug) { + this.kFilter = kFilter; + this.isDebug = isDebug; + this.currentAccelMeasurement = new SensorSample(); + this.currentGyroMeasurement = new SensorSample(); + this.previousGyroMeasurement = new SensorSample(); + if (isIOS()) { + this.filterQ = new Quaternion(-1, 0, 0, 1); + } else { + this.filterQ = new Quaternion(1, 0, 0, 1); + } + this.previousFilterQ = new Quaternion(); + this.previousFilterQ.copy(this.filterQ); + this.accelQ = new Quaternion(); + this.isOrientationInitialized = false; + this.estimatedGravity = new Vector3(); + this.measuredGravity = new Vector3(); + this.gyroIntegralQ = new Quaternion(); +} +ComplementaryFilter.prototype.addAccelMeasurement = function (vector, timestampS) { + this.currentAccelMeasurement.set(vector, timestampS); +}; +ComplementaryFilter.prototype.addGyroMeasurement = function (vector, timestampS) { + this.currentGyroMeasurement.set(vector, timestampS); + var deltaT = timestampS - this.previousGyroMeasurement.timestampS; + if (isTimestampDeltaValid(deltaT)) { + this.run_(); + } + this.previousGyroMeasurement.copy(this.currentGyroMeasurement); +}; +ComplementaryFilter.prototype.run_ = function () { + if (!this.isOrientationInitialized) { + this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample); + this.previousFilterQ.copy(this.accelQ); + this.isOrientationInitialized = true; + return; + } + var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; + var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT); + this.gyroIntegralQ.multiply(gyroDeltaQ); + this.filterQ.copy(this.previousFilterQ); + this.filterQ.multiply(gyroDeltaQ); + var invFilterQ = new Quaternion(); + invFilterQ.copy(this.filterQ); + invFilterQ.inverse(); + this.estimatedGravity.set(0, 0, -1); + this.estimatedGravity.applyQuaternion(invFilterQ); + this.estimatedGravity.normalize(); + this.measuredGravity.copy(this.currentAccelMeasurement.sample); + this.measuredGravity.normalize(); + var deltaQ = new Quaternion(); + deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity); + deltaQ.inverse(); + if (this.isDebug) { + console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', radToDeg * getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1)); + } + var targetQ = new Quaternion(); + targetQ.copy(this.filterQ); + targetQ.multiply(deltaQ); + this.filterQ.slerp(targetQ, 1 - this.kFilter); + this.previousFilterQ.copy(this.filterQ); +}; +ComplementaryFilter.prototype.getOrientation = function () { + return this.filterQ; +}; +ComplementaryFilter.prototype.accelToQuaternion_ = function (accel) { + var normAccel = new Vector3(); + normAccel.copy(accel); + normAccel.normalize(); + var quat = new Quaternion(); + quat.setFromUnitVectors(new Vector3(0, 0, -1), normAccel); + quat.inverse(); + return quat; +}; +ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function (gyro, dt) { + var quat = new Quaternion(); + var axis = new Vector3(); + axis.copy(gyro); + axis.normalize(); + quat.setFromAxisAngle(axis, gyro.length() * dt); + return quat; +}; +function PosePredictor(predictionTimeS, isDebug) { + this.predictionTimeS = predictionTimeS; + this.isDebug = isDebug; + this.previousQ = new Quaternion(); + this.previousTimestampS = null; + this.deltaQ = new Quaternion(); + this.outQ = new Quaternion(); +} +PosePredictor.prototype.getPrediction = function (currentQ, gyro, timestampS) { + if (!this.previousTimestampS) { + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return currentQ; + } + var axis = new Vector3(); + axis.copy(gyro); + axis.normalize(); + var angularSpeed = gyro.length(); + if (angularSpeed < degToRad * 20) { + if (this.isDebug) { + console.log('Moving slowly, at %s deg/s: no prediction', (radToDeg * angularSpeed).toFixed(1)); + } + this.outQ.copy(currentQ); + this.previousQ.copy(currentQ); + return this.outQ; + } + var predictAngle = angularSpeed * this.predictionTimeS; + this.deltaQ.setFromAxisAngle(axis, predictAngle); + this.outQ.copy(this.previousQ); + this.outQ.multiply(this.deltaQ); + this.previousQ.copy(currentQ); + this.previousTimestampS = timestampS; + return this.outQ; +}; +function FusionPoseSensor(kFilter, predictionTime, yawOnly, isDebug) { + this.yawOnly = yawOnly; + this.accelerometer = new Vector3(); + this.gyroscope = new Vector3(); + this.filter = new ComplementaryFilter(kFilter, isDebug); + this.posePredictor = new PosePredictor(predictionTime, isDebug); + this.isFirefoxAndroid = isFirefoxAndroid(); + this.isIOS = isIOS(); + var chromeVersion = getChromeVersion(); + this.isDeviceMotionInRadians = !this.isIOS && chromeVersion && chromeVersion < 66; + this.isWithoutDeviceMotion = isChromeWithoutDeviceMotion(); + this.filterToWorldQ = new Quaternion(); + if (isIOS()) { + this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + } else { + this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); + } + this.inverseWorldToScreenQ = new Quaternion(); + this.worldToScreenQ = new Quaternion(); + this.originalPoseAdjustQ = new Quaternion(); + this.originalPoseAdjustQ.setFromAxisAngle(new Vector3(0, 0, 1), -window.orientation * Math.PI / 180); + this.setScreenTransform_(); + if (isLandscapeMode()) { + this.filterToWorldQ.multiply(this.inverseWorldToScreenQ); + } + this.resetQ = new Quaternion(); + this.orientationOut_ = new Float32Array(4); + this.start(); +} +FusionPoseSensor.prototype.getPosition = function () { + return null; +}; +FusionPoseSensor.prototype.getOrientation = function () { + var orientation = void 0; + if (this.isWithoutDeviceMotion && this._deviceOrientationQ) { + this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () { + var z = new Quaternion().setFromAxisAngle(new Vector3(0, 0, -1), 0); + var y = new Quaternion(); + if (window.orientation === -90) { + y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / -2); + } else { + y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2); + } + return z.multiply(y); + }(); + this.deviceOrientationFilterToWorldQ = this.deviceOrientationFilterToWorldQ || function () { + var q = new Quaternion(); + q.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); + return q; + }(); + orientation = this._deviceOrientationQ; + var out = new Quaternion(); + out.copy(orientation); + out.multiply(this.deviceOrientationFilterToWorldQ); + out.multiply(this.resetQ); + out.multiply(this.worldToScreenQ); + out.multiplyQuaternions(this.deviceOrientationFixQ, out); + if (this.yawOnly) { + out.x = 0; + out.z = 0; + out.normalize(); + } + this.orientationOut_[0] = out.x; + this.orientationOut_[1] = out.y; + this.orientationOut_[2] = out.z; + this.orientationOut_[3] = out.w; + return this.orientationOut_; + } else { + var filterOrientation = this.filter.getOrientation(); + orientation = this.posePredictor.getPrediction(filterOrientation, this.gyroscope, this.previousTimestampS); + } + var out = new Quaternion(); + out.copy(this.filterToWorldQ); + out.multiply(this.resetQ); + out.multiply(orientation); + out.multiply(this.worldToScreenQ); + if (this.yawOnly) { + out.x = 0; + out.z = 0; + out.normalize(); + } + this.orientationOut_[0] = out.x; + this.orientationOut_[1] = out.y; + this.orientationOut_[2] = out.z; + this.orientationOut_[3] = out.w; + return this.orientationOut_; +}; +FusionPoseSensor.prototype.resetPose = function () { + this.resetQ.copy(this.filter.getOrientation()); + this.resetQ.x = 0; + this.resetQ.y = 0; + this.resetQ.z *= -1; + this.resetQ.normalize(); + if (isLandscapeMode()) { + this.resetQ.multiply(this.inverseWorldToScreenQ); + } + this.resetQ.multiply(this.originalPoseAdjustQ); +}; +FusionPoseSensor.prototype.onDeviceOrientation_ = function (e) { + this._deviceOrientationQ = this._deviceOrientationQ || new Quaternion(); + var alpha = e.alpha, + beta = e.beta, + gamma = e.gamma; + alpha = (alpha || 0) * Math.PI / 180; + beta = (beta || 0) * Math.PI / 180; + gamma = (gamma || 0) * Math.PI / 180; + this._deviceOrientationQ.setFromEulerYXZ(beta, alpha, -gamma); +}; +FusionPoseSensor.prototype.onDeviceMotion_ = function (deviceMotion) { + this.updateDeviceMotion_(deviceMotion); +}; +FusionPoseSensor.prototype.updateDeviceMotion_ = function (deviceMotion) { + var accGravity = deviceMotion.accelerationIncludingGravity; + var rotRate = deviceMotion.rotationRate; + var timestampS = deviceMotion.timeStamp / 1000; + var deltaS = timestampS - this.previousTimestampS; + if (deltaS < 0) { + warnOnce('fusion-pose-sensor:invalid:non-monotonic', 'Invalid timestamps detected: non-monotonic timestamp from devicemotion'); + this.previousTimestampS = timestampS; + return; + } else if (deltaS <= MIN_TIMESTEP || deltaS > MAX_TIMESTEP) { + warnOnce('fusion-pose-sensor:invalid:outside-threshold', 'Invalid timestamps detected: Timestamp from devicemotion outside expected range.'); + this.previousTimestampS = timestampS; + return; + } + this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); + if (isR7()) { + this.gyroscope.set(-rotRate.beta, rotRate.alpha, rotRate.gamma); + } else { + this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); + } + if (!this.isDeviceMotionInRadians) { + this.gyroscope.multiplyScalar(Math.PI / 180); + } + this.filter.addAccelMeasurement(this.accelerometer, timestampS); + this.filter.addGyroMeasurement(this.gyroscope, timestampS); + this.previousTimestampS = timestampS; +}; +FusionPoseSensor.prototype.onOrientationChange_ = function (screenOrientation) { + this.setScreenTransform_(); +}; +FusionPoseSensor.prototype.onMessage_ = function (event) { + var message = event.data; + if (!message || !message.type) { + return; + } + var type = message.type.toLowerCase(); + if (type !== 'devicemotion') { + return; + } + this.updateDeviceMotion_(message.deviceMotionEvent); +}; +FusionPoseSensor.prototype.setScreenTransform_ = function () { + this.worldToScreenQ.set(0, 0, 0, 1); + switch (window.orientation) { + case 0: + break; + case 90: + this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), -Math.PI / 2); + break; + case -90: + this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); + break; + case 180: + break; + } + this.inverseWorldToScreenQ.copy(this.worldToScreenQ); + this.inverseWorldToScreenQ.inverse(); +}; +FusionPoseSensor.prototype.start = function () { + this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this); + this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this); + this.onMessageCallback_ = this.onMessage_.bind(this); + this.onDeviceOrientationCallback_ = this.onDeviceOrientation_.bind(this); + if (isIOS() && isInsideCrossOriginIFrame()) { + window.addEventListener('message', this.onMessageCallback_); + } + window.addEventListener('orientationchange', this.onOrientationChangeCallback_); + if (this.isWithoutDeviceMotion) { + window.addEventListener('deviceorientation', this.onDeviceOrientationCallback_); + } else { + window.addEventListener('devicemotion', this.onDeviceMotionCallback_); + } +}; +FusionPoseSensor.prototype.stop = function () { + window.removeEventListener('devicemotion', this.onDeviceMotionCallback_); + window.removeEventListener('deviceorientation', this.onDeviceOrientationCallback_); + window.removeEventListener('orientationchange', this.onOrientationChangeCallback_); + window.removeEventListener('message', this.onMessageCallback_); +}; +var SENSOR_FREQUENCY = 60; +var X_AXIS = new Vector3(1, 0, 0); +var Z_AXIS = new Vector3(0, 0, 1); +var SENSOR_TO_VR = new Quaternion(); +SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2); +SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2)); +var PoseSensor = function () { + function PoseSensor(config) { + classCallCheck(this, PoseSensor); + this.config = config; + this.sensor = null; + this.fusionSensor = null; + this._out = new Float32Array(4); + this.api = null; + this.errors = []; + this._sensorQ = new Quaternion(); + this._outQ = new Quaternion(); + this._onSensorRead = this._onSensorRead.bind(this); + this._onSensorError = this._onSensorError.bind(this); + this.init(); + } + createClass(PoseSensor, [{ + key: 'init', + value: function init() { + var sensor = null; + try { + sensor = new RelativeOrientationSensor({ + frequency: SENSOR_FREQUENCY, + referenceFrame: 'screen' + }); + sensor.addEventListener('error', this._onSensorError); + } catch (error) { + this.errors.push(error); + if (error.name === 'SecurityError') { + console.error('Cannot construct sensors due to the Feature Policy'); + console.warn('Attempting to fall back using "devicemotion"; however this will ' + 'fail in the future without correct permissions.'); + this.useDeviceMotion(); + } else if (error.name === 'ReferenceError') { + this.useDeviceMotion(); + } else { + console.error(error); + } + } + if (sensor) { + this.api = 'sensor'; + this.sensor = sensor; + this.sensor.addEventListener('reading', this._onSensorRead); + this.sensor.start(); + } + } + }, { + key: 'useDeviceMotion', + value: function useDeviceMotion() { + this.api = 'devicemotion'; + this.fusionSensor = new FusionPoseSensor(this.config.K_FILTER, this.config.PREDICTION_TIME_S, this.config.YAW_ONLY, this.config.DEBUG); + if (this.sensor) { + this.sensor.removeEventListener('reading', this._onSensorRead); + this.sensor.removeEventListener('error', this._onSensorError); + this.sensor = null; + } + } + }, { + key: 'getOrientation', + value: function getOrientation() { + if (this.fusionSensor) { + return this.fusionSensor.getOrientation(); + } + if (!this.sensor || !this.sensor.quaternion) { + this._out[0] = this._out[1] = this._out[2] = 0; + this._out[3] = 1; + return this._out; + } + var q = this.sensor.quaternion; + this._sensorQ.set(q[0], q[1], q[2], q[3]); + var out = this._outQ; + out.copy(SENSOR_TO_VR); + out.multiply(this._sensorQ); + if (this.config.YAW_ONLY) { + out.x = out.z = 0; + out.normalize(); + } + this._out[0] = out.x; + this._out[1] = out.y; + this._out[2] = out.z; + this._out[3] = out.w; + return this._out; + } + }, { + key: '_onSensorError', + value: function _onSensorError(event) { + this.errors.push(event.error); + if (event.error.name === 'NotAllowedError') { + console.error('Permission to access sensor was denied'); + } else if (event.error.name === 'NotReadableError') { + console.error('Sensor could not be read'); + } else { + console.error(event.error); + } + this.useDeviceMotion(); + } + }, { + key: '_onSensorRead', + value: function _onSensorRead() {} + }]); + return PoseSensor; +}(); +var rotateInstructionsAsset = "<svg width='198' height='240' viewBox='0 0 198 240' xmlns='http://www.w3.org/2000/svg'><g fill='none' fill-rule='evenodd'><path d='M149.625 109.527l6.737 3.891v.886c0 .177.013.36.038.549.01.081.02.162.027.242.14 1.415.974 2.998 2.105 3.999l5.72 5.062.081-.09s4.382-2.53 5.235-3.024l25.97 14.993v54.001c0 .771-.386 1.217-.948 1.217-.233 0-.495-.076-.772-.236l-23.967-13.838-.014.024-27.322 15.775-.85-1.323c-4.731-1.529-9.748-2.74-14.951-3.61a.27.27 0 0 0-.007.024l-5.067 16.961-7.891 4.556-.037-.063v27.59c0 .772-.386 1.217-.948 1.217-.232 0-.495-.076-.772-.236l-42.473-24.522c-.95-.549-1.72-1.877-1.72-2.967v-1.035l-.021.047a5.111 5.111 0 0 0-1.816-.399 5.682 5.682 0 0 0-.546.001 13.724 13.724 0 0 1-1.918-.041c-1.655-.153-3.2-.6-4.404-1.296l-46.576-26.89.005.012-10.278-18.75c-1.001-1.827-.241-4.216 1.698-5.336l56.011-32.345a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.659 3.227 1.853l.005-.003.227.413-.006.004a9.63 9.63 0 0 0 1.477 2.018l.277.27c1.914 1.85 4.468 2.801 7.113 2.801 1.949 0 3.948-.517 5.775-1.572.013 0 7.319-4.219 7.319-4.219a4.194 4.194 0 0 1 2.099-.572c1.326 0 2.572.658 3.226 1.853l3.25 5.928.022-.018 6.785 3.917-.105-.182 46.881-26.965m0-1.635c-.282 0-.563.073-.815.218l-46.169 26.556-5.41-3.124-3.005-5.481c-.913-1.667-2.699-2.702-4.66-2.703-1.011 0-2.02.274-2.917.792a3825 3825 0 0 1-7.275 4.195l-.044.024a9.937 9.937 0 0 1-4.957 1.353c-2.292 0-4.414-.832-5.976-2.342l-.252-.245a7.992 7.992 0 0 1-1.139-1.534 1.379 1.379 0 0 0-.06-.122l-.227-.414a1.718 1.718 0 0 0-.095-.154c-.938-1.574-2.673-2.545-4.571-2.545-1.011 0-2.02.274-2.917.792L3.125 155.502c-2.699 1.559-3.738 4.94-2.314 7.538l10.278 18.75c.177.323.448.563.761.704l46.426 26.804c1.403.81 3.157 1.332 5.072 1.508a15.661 15.661 0 0 0 2.146.046 4.766 4.766 0 0 1 .396 0c.096.004.19.011.283.022.109 1.593 1.159 3.323 2.529 4.114l42.472 24.522c.524.302 1.058.455 1.59.455 1.497 0 2.583-1.2 2.583-2.852v-26.562l7.111-4.105a1.64 1.64 0 0 0 .749-.948l4.658-15.593c4.414.797 8.692 1.848 12.742 3.128l.533.829a1.634 1.634 0 0 0 2.193.531l26.532-15.317L193 192.433c.523.302 1.058.455 1.59.455 1.497 0 2.583-1.199 2.583-2.852v-54.001c0-.584-.312-1.124-.818-1.416l-25.97-14.993a1.633 1.633 0 0 0-1.636.001c-.606.351-2.993 1.73-4.325 2.498l-4.809-4.255c-.819-.725-1.461-1.933-1.561-2.936a7.776 7.776 0 0 0-.033-.294 2.487 2.487 0 0 1-.023-.336v-.886c0-.584-.312-1.123-.817-1.416l-6.739-3.891a1.633 1.633 0 0 0-.817-.219' fill='#455A64'/><path d='M96.027 132.636l46.576 26.891c1.204.695 1.979 1.587 2.242 2.541l-.01.007-81.374 46.982h-.001c-1.654-.152-3.199-.6-4.403-1.295l-46.576-26.891 83.546-48.235' fill='#FAFAFA'/><path d='M63.461 209.174c-.008 0-.015 0-.022-.002-1.693-.156-3.228-.609-4.441-1.309l-46.576-26.89a.118.118 0 0 1 0-.203l83.546-48.235a.117.117 0 0 1 .117 0l46.576 26.891c1.227.708 2.021 1.612 2.296 2.611a.116.116 0 0 1-.042.124l-.021.016-81.375 46.981a.11.11 0 0 1-.058.016zm-50.747-28.303l46.401 26.79c1.178.68 2.671 1.121 4.32 1.276l81.272-46.922c-.279-.907-1.025-1.73-2.163-2.387l-46.517-26.857-83.313 48.1z' fill='#607D8B'/><path d='M148.327 165.471a5.85 5.85 0 0 1-.546.001c-1.894-.083-3.302-1.038-3.145-2.132a2.693 2.693 0 0 0-.072-1.105l-81.103 46.822c.628.058 1.272.073 1.918.042.182-.009.364-.009.546-.001 1.894.083 3.302 1.038 3.145 2.132l79.257-45.759' fill='#FFF'/><path d='M69.07 211.347a.118.118 0 0 1-.115-.134c.045-.317-.057-.637-.297-.925-.505-.61-1.555-1.022-2.738-1.074a5.966 5.966 0 0 0-.535.001 14.03 14.03 0 0 1-1.935-.041.117.117 0 0 1-.103-.092.116.116 0 0 1 .055-.126l81.104-46.822a.117.117 0 0 1 .171.07c.104.381.129.768.074 1.153-.045.316.057.637.296.925.506.61 1.555 1.021 2.739 1.073.178.008.357.008.535-.001a.117.117 0 0 1 .064.218l-79.256 45.759a.114.114 0 0 1-.059.016zm-3.405-2.372c.089 0 .177.002.265.006 1.266.056 2.353.488 2.908 1.158.227.274.35.575.36.882l78.685-45.429c-.036 0-.072-.001-.107-.003-1.267-.056-2.354-.489-2.909-1.158-.282-.34-.402-.724-.347-1.107a2.604 2.604 0 0 0-.032-.91L63.846 208.97a13.91 13.91 0 0 0 1.528.012c.097-.005.194-.007.291-.007z' fill='#607D8B'/><path d='M2.208 162.134c-1.001-1.827-.241-4.217 1.698-5.337l56.011-32.344c1.939-1.12 4.324-.546 5.326 1.281l.232.41a9.344 9.344 0 0 0 1.47 2.021l.278.27c3.325 3.214 8.583 3.716 12.888 1.23l7.319-4.22c1.94-1.119 4.324-.546 5.325 1.282l3.25 5.928-83.519 48.229-10.278-18.75z' fill='#FAFAFA'/><path d='M12.486 181.001a.112.112 0 0 1-.031-.005.114.114 0 0 1-.071-.056L2.106 162.19c-1.031-1.88-.249-4.345 1.742-5.494l56.01-32.344a4.328 4.328 0 0 1 2.158-.588c1.415 0 2.65.702 3.311 1.882.01.008.018.017.024.028l.227.414a.122.122 0 0 1 .013.038 9.508 9.508 0 0 0 1.439 1.959l.275.266c1.846 1.786 4.344 2.769 7.031 2.769 1.977 0 3.954-.538 5.717-1.557a.148.148 0 0 1 .035-.013l7.284-4.206a4.321 4.321 0 0 1 2.157-.588c1.427 0 2.672.716 3.329 1.914l3.249 5.929a.116.116 0 0 1-.044.157l-83.518 48.229a.116.116 0 0 1-.059.016zm49.53-57.004c-.704 0-1.41.193-2.041.557l-56.01 32.345c-1.882 1.086-2.624 3.409-1.655 5.179l10.221 18.645 83.317-48.112-3.195-5.829c-.615-1.122-1.783-1.792-3.124-1.792a4.08 4.08 0 0 0-2.04.557l-7.317 4.225a.148.148 0 0 1-.035.013 11.7 11.7 0 0 1-5.801 1.569c-2.748 0-5.303-1.007-7.194-2.835l-.278-.27a9.716 9.716 0 0 1-1.497-2.046.096.096 0 0 1-.013-.037l-.191-.347a.11.11 0 0 1-.023-.029c-.615-1.123-1.783-1.793-3.124-1.793z' fill='#607D8B'/><path d='M42.434 155.808c-2.51-.001-4.697-1.258-5.852-3.365-1.811-3.304-.438-7.634 3.059-9.654l12.291-7.098a7.599 7.599 0 0 1 3.789-1.033c2.51 0 4.697 1.258 5.852 3.365 1.811 3.304.439 7.634-3.059 9.654l-12.291 7.098a7.606 7.606 0 0 1-3.789 1.033zm13.287-20.683a7.128 7.128 0 0 0-3.555.971l-12.291 7.098c-3.279 1.893-4.573 5.942-2.883 9.024 1.071 1.955 3.106 3.122 5.442 3.122a7.13 7.13 0 0 0 3.556-.97l12.291-7.098c3.279-1.893 4.572-5.942 2.883-9.024-1.072-1.955-3.106-3.123-5.443-3.123z' fill='#607D8B'/><path d='M149.588 109.407l6.737 3.89v.887c0 .176.013.36.037.549.011.081.02.161.028.242.14 1.415.973 2.998 2.105 3.999l7.396 6.545c.177.156.358.295.541.415 1.579 1.04 2.95.466 3.062-1.282.049-.784.057-1.595.023-2.429l-.003-.16v-1.151l25.987 15.003v54c0 1.09-.77 1.53-1.72.982l-42.473-24.523c-.95-.548-1.72-1.877-1.72-2.966v-34.033' fill='#FAFAFA'/><path d='M194.553 191.25c-.257 0-.54-.085-.831-.253l-42.472-24.521c-.981-.567-1.779-1.943-1.779-3.068v-34.033h.234v34.033c0 1.051.745 2.336 1.661 2.866l42.473 24.521c.424.245.816.288 1.103.122.285-.164.442-.52.442-1.002v-53.933l-25.753-14.868.003 1.106c.034.832.026 1.654-.024 2.439-.054.844-.396 1.464-.963 1.746-.619.309-1.45.173-2.28-.373a5.023 5.023 0 0 1-.553-.426l-7.397-6.544c-1.158-1.026-1.999-2.625-2.143-4.076a9.624 9.624 0 0 0-.027-.238 4.241 4.241 0 0 1-.038-.564v-.82l-6.68-3.856.117-.202 6.738 3.89.058.034v.954c0 .171.012.351.036.533.011.083.021.165.029.246.138 1.395.948 2.935 2.065 3.923l7.397 6.545c.173.153.35.289.527.406.758.499 1.504.63 2.047.359.49-.243.786-.795.834-1.551.05-.778.057-1.591.024-2.417l-.004-.163v-1.355l.175.1 25.987 15.004.059.033v54.068c0 .569-.198.996-.559 1.204a1.002 1.002 0 0 1-.506.131' fill='#607D8B'/><path d='M145.685 163.161l24.115 13.922-25.978 14.998-1.462-.307c-6.534-2.17-13.628-3.728-21.019-4.616-4.365-.524-8.663 1.096-9.598 3.62a2.746 2.746 0 0 0-.011 1.928c1.538 4.267 4.236 8.363 7.995 12.135l.532.845-25.977 14.997-24.115-13.922 75.518-43.6' fill='#FFF'/><path d='M94.282 220.818l-.059-.033-24.29-14.024.175-.101 75.577-43.634.058.033 24.29 14.024-26.191 15.122-.045-.01-1.461-.307c-6.549-2.174-13.613-3.725-21.009-4.614a13.744 13.744 0 0 0-1.638-.097c-3.758 0-7.054 1.531-7.837 3.642a2.62 2.62 0 0 0-.01 1.848c1.535 4.258 4.216 8.326 7.968 12.091l.016.021.526.835.006.01.064.102-.105.061-25.977 14.998-.058.033zm-23.881-14.057l23.881 13.788 24.802-14.32c.546-.315.846-.489 1.017-.575l-.466-.74c-3.771-3.787-6.467-7.881-8.013-12.168a2.851 2.851 0 0 1 .011-2.008c.815-2.199 4.203-3.795 8.056-3.795.557 0 1.117.033 1.666.099 7.412.891 14.491 2.445 21.041 4.621.836.175 1.215.254 1.39.304l25.78-14.884-23.881-13.788-75.284 43.466z' fill='#607D8B'/><path d='M167.23 125.979v50.871l-27.321 15.773-6.461-14.167c-.91-1.996-3.428-1.738-5.624.574a10.238 10.238 0 0 0-2.33 4.018l-6.46 21.628-27.322 15.774v-50.871l75.518-43.6' fill='#FFF'/><path d='M91.712 220.567a.127.127 0 0 1-.059-.016.118.118 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.519-43.6a.117.117 0 0 1 .175.101v50.871c0 .041-.023.08-.059.1l-27.321 15.775a.118.118 0 0 1-.094.01.12.12 0 0 1-.071-.063l-6.46-14.168c-.375-.822-1.062-1.275-1.934-1.275-1.089 0-2.364.686-3.5 1.881a10.206 10.206 0 0 0-2.302 3.972l-6.46 21.627a.118.118 0 0 1-.054.068L91.77 220.551a.12.12 0 0 1-.058.016zm.117-50.92v50.601l27.106-15.65 6.447-21.583a10.286 10.286 0 0 1 2.357-4.065c1.18-1.242 2.517-1.954 3.669-1.954.969 0 1.731.501 2.146 1.411l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M168.543 126.213v50.87l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.624.574a10.248 10.248 0 0 0-2.33 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6' fill='#FFF'/><path d='M93.025 220.8a.123.123 0 0 1-.059-.015.12.12 0 0 1-.058-.101v-50.871c0-.042.023-.08.058-.101l75.518-43.6a.112.112 0 0 1 .117 0c.036.02.059.059.059.1v50.871a.116.116 0 0 1-.059.101l-27.321 15.774a.111.111 0 0 1-.094.01.115.115 0 0 1-.071-.062l-6.46-14.168c-.375-.823-1.062-1.275-1.935-1.275-1.088 0-2.363.685-3.499 1.881a10.19 10.19 0 0 0-2.302 3.971l-6.461 21.628a.108.108 0 0 1-.053.067l-27.322 15.775a.12.12 0 0 1-.058.015zm.117-50.919v50.6l27.106-15.649 6.447-21.584a10.293 10.293 0 0 1 2.357-4.065c1.179-1.241 2.516-1.954 3.668-1.954.969 0 1.732.502 2.147 1.412l6.407 14.051 27.152-15.676v-50.601l-75.284 43.466z' fill='#607D8B'/><path d='M169.8 177.083l-27.322 15.774-6.46-14.168c-.91-1.995-3.428-1.738-5.625.574a10.246 10.246 0 0 0-2.329 4.019l-6.461 21.627-27.321 15.774v-50.87l75.518-43.6v50.87z' fill='#FAFAFA'/><path d='M94.282 220.917a.234.234 0 0 1-.234-.233v-50.871c0-.083.045-.161.117-.202l75.518-43.601a.234.234 0 1 1 .35.202v50.871a.233.233 0 0 1-.116.202l-27.322 15.775a.232.232 0 0 1-.329-.106l-6.461-14.168c-.36-.789-.992-1.206-1.828-1.206-1.056 0-2.301.672-3.415 1.844a10.099 10.099 0 0 0-2.275 3.924l-6.46 21.628a.235.235 0 0 1-.107.136l-27.322 15.774a.23.23 0 0 1-.116.031zm.233-50.969v50.331l26.891-15.525 6.434-21.539a10.41 10.41 0 0 1 2.384-4.112c1.201-1.265 2.569-1.991 3.753-1.991 1.018 0 1.818.526 2.253 1.48l6.354 13.934 26.982-15.578v-50.331l-75.051 43.331z' fill='#607D8B'/><path d='M109.894 199.943c-1.774 0-3.241-.725-4.244-2.12a.224.224 0 0 1 .023-.294.233.233 0 0 1 .301-.023c.78.547 1.705.827 2.75.827 1.323 0 2.754-.439 4.256-1.306 5.311-3.067 9.631-10.518 9.631-16.611 0-1.927-.442-3.56-1.278-4.724a.232.232 0 0 1 .323-.327c1.671 1.172 2.591 3.381 2.591 6.219 0 6.242-4.426 13.863-9.865 17.003-1.574.908-3.084 1.356-4.488 1.356zm-2.969-1.542c.813.651 1.82.877 2.968.877h.001c1.321 0 2.753-.327 4.254-1.194 5.311-3.067 9.632-10.463 9.632-16.556 0-1.979-.463-3.599-1.326-4.761.411 1.035.625 2.275.625 3.635 0 6.243-4.426 13.883-9.865 17.023-1.574.909-3.084 1.317-4.49 1.317-.641 0-1.243-.149-1.799-.341z' fill='#607D8B'/><path d='M113.097 197.23c5.384-3.108 9.748-10.636 9.748-16.814 0-2.051-.483-3.692-1.323-4.86-1.784-1.252-4.374-1.194-7.257.47-5.384 3.108-9.748 10.636-9.748 16.814 0 2.051.483 3.692 1.323 4.86 1.784 1.252 4.374 1.194 7.257-.47' fill='#FAFAFA'/><path d='M108.724 198.614c-1.142 0-2.158-.213-3.019-.817-.021-.014-.04.014-.055-.007-.894-1.244-1.367-2.948-1.367-4.973 0-6.242 4.426-13.864 9.865-17.005 1.574-.908 3.084-1.363 4.49-1.363 1.142 0 2.158.309 3.018.913a.23.23 0 0 1 .056.056c.894 1.244 1.367 2.972 1.367 4.997 0 6.243-4.426 13.783-9.865 16.923-1.574.909-3.084 1.276-4.49 1.276zm-2.718-1.109c.774.532 1.688.776 2.718.776 1.323 0 2.754-.413 4.256-1.28 5.311-3.066 9.631-10.505 9.631-16.598 0-1.909-.434-3.523-1.255-4.685-.774-.533-1.688-.799-2.718-.799-1.323 0-2.755.441-4.256 1.308-5.311 3.066-9.631 10.506-9.631 16.599 0 1.909.434 3.517 1.255 4.679z' fill='#607D8B'/><path d='M149.318 114.262l-9.984 8.878 15.893 11.031 5.589-6.112-11.498-13.797' fill='#FAFAFA'/><path d='M169.676 120.84l-9.748 5.627c-3.642 2.103-9.528 2.113-13.147.024-3.62-2.089-3.601-5.488.041-7.591l9.495-5.608-6.729-3.885-81.836 47.071 45.923 26.514 3.081-1.779c.631-.365.869-.898.618-1.39-2.357-4.632-2.593-9.546-.683-14.262 5.638-13.92 24.509-24.815 48.618-28.07 8.169-1.103 16.68-.967 24.704.394.852.145 1.776.008 2.407-.357l3.081-1.778-25.825-14.91' fill='#FAFAFA'/><path d='M113.675 183.459a.47.47 0 0 1-.233-.062l-45.924-26.515a.468.468 0 0 1 .001-.809l81.836-47.071a.467.467 0 0 1 .466 0l6.729 3.885a.467.467 0 0 1-.467.809l-6.496-3.75-80.9 46.533 44.988 25.973 2.848-1.644c.192-.111.62-.409.435-.773-2.416-4.748-2.658-9.814-.7-14.65 2.806-6.927 8.885-13.242 17.582-18.263 8.657-4.998 19.518-8.489 31.407-10.094 8.198-1.107 16.79-.97 24.844.397.739.125 1.561.007 2.095-.301l2.381-1.374-25.125-14.506a.467.467 0 0 1 .467-.809l25.825 14.91a.467.467 0 0 1 0 .809l-3.081 1.779c-.721.417-1.763.575-2.718.413-7.963-1.351-16.457-1.486-24.563-.392-11.77 1.589-22.512 5.039-31.065 9.977-8.514 4.916-14.456 11.073-17.183 17.805-1.854 4.578-1.623 9.376.666 13.875.37.725.055 1.513-.8 2.006l-3.081 1.78a.476.476 0 0 1-.234.062' fill='#455A64'/><path d='M153.316 128.279c-2.413 0-4.821-.528-6.652-1.586-1.818-1.049-2.82-2.461-2.82-3.975 0-1.527 1.016-2.955 2.861-4.02l9.493-5.607a.233.233 0 1 1 .238.402l-9.496 5.609c-1.696.979-2.628 2.263-2.628 3.616 0 1.34.918 2.608 2.585 3.571 3.549 2.049 9.343 2.038 12.914-.024l9.748-5.628a.234.234 0 0 1 .234.405l-9.748 5.628c-1.858 1.072-4.296 1.609-6.729 1.609' fill='#607D8B'/><path d='M113.675 182.992l-45.913-26.508M113.675 183.342a.346.346 0 0 1-.175-.047l-45.913-26.508a.35.35 0 1 1 .35-.607l45.913 26.508a.35.35 0 0 1-.175.654' fill='#455A64'/><path d='M67.762 156.484v54.001c0 1.09.77 2.418 1.72 2.967l42.473 24.521c.95.549 1.72.11 1.72-.98v-54.001' fill='#FAFAFA'/><path d='M112.727 238.561c-.297 0-.62-.095-.947-.285l-42.473-24.521c-1.063-.613-1.895-2.05-1.895-3.27v-54.001a.35.35 0 1 1 .701 0v54.001c0 .96.707 2.18 1.544 2.663l42.473 24.522c.344.198.661.243.87.122.206-.119.325-.411.325-.799v-54.001a.35.35 0 1 1 .7 0v54.001c0 .655-.239 1.154-.675 1.406a1.235 1.235 0 0 1-.623.162' fill='#455A64'/><path d='M112.86 147.512h-.001c-2.318 0-4.499-.522-6.142-1.471-1.705-.984-2.643-2.315-2.643-3.749 0-1.445.952-2.791 2.68-3.788l12.041-6.953c1.668-.962 3.874-1.493 6.212-1.493 2.318 0 4.499.523 6.143 1.472 1.704.984 2.643 2.315 2.643 3.748 0 1.446-.952 2.791-2.68 3.789l-12.042 6.952c-1.668.963-3.874 1.493-6.211 1.493zm12.147-16.753c-2.217 0-4.298.497-5.861 1.399l-12.042 6.952c-1.502.868-2.33 1.998-2.33 3.182 0 1.173.815 2.289 2.293 3.142 1.538.889 3.596 1.378 5.792 1.378h.001c2.216 0 4.298-.497 5.861-1.399l12.041-6.953c1.502-.867 2.33-1.997 2.33-3.182 0-1.172-.814-2.288-2.292-3.142-1.539-.888-3.596-1.377-5.793-1.377z' fill='#607D8B'/><path d='M165.63 123.219l-5.734 3.311c-3.167 1.828-8.286 1.837-11.433.02-3.147-1.817-3.131-4.772.036-6.601l5.734-3.31 11.397 6.58' fill='#FAFAFA'/><path d='M154.233 117.448l9.995 5.771-4.682 2.704c-1.434.827-3.352 1.283-5.399 1.283-2.029 0-3.923-.449-5.333-1.263-1.29-.744-2-1.694-2-2.674 0-.991.723-1.955 2.036-2.713l5.383-3.108m0-.809l-5.734 3.31c-3.167 1.829-3.183 4.784-.036 6.601 1.568.905 3.623 1.357 5.684 1.357 2.077 0 4.159-.46 5.749-1.377l5.734-3.311-11.397-6.58M145.445 179.667c-1.773 0-3.241-.85-4.243-2.245-.067-.092-.057-.275.023-.356.08-.081.207-.12.3-.055.781.548 1.706.812 2.751.811 1.322 0 2.754-.446 4.256-1.313 5.31-3.066 9.631-10.522 9.631-16.615 0-1.927-.442-3.562-1.279-4.726a.235.235 0 0 1 .024-.301.232.232 0 0 1 .3-.027c1.67 1.172 2.59 3.38 2.59 6.219 0 6.242-4.425 13.987-9.865 17.127-1.573.908-3.083 1.481-4.488 1.481zM142.476 178c.814.651 1.82 1.002 2.969 1.002 1.322 0 2.753-.452 4.255-1.32 5.31-3.065 9.631-10.523 9.631-16.617 0-1.98-.463-3.63-1.325-4.793.411 1.035.624 2.26.624 3.62 0 6.242-4.425 13.875-9.865 17.015-1.573.909-3.084 1.376-4.489 1.376a5.49 5.49 0 0 1-1.8-.283z' fill='#607D8B'/><path d='M148.648 176.704c5.384-3.108 9.748-10.636 9.748-16.813 0-2.052-.483-3.693-1.322-4.861-1.785-1.252-4.375-1.194-7.258.471-5.383 3.108-9.748 10.636-9.748 16.813 0 2.051.484 3.692 1.323 4.86 1.785 1.253 4.374 1.195 7.257-.47' fill='#FAFAFA'/><path d='M144.276 178.276c-1.143 0-2.158-.307-3.019-.911a.217.217 0 0 1-.055-.054c-.895-1.244-1.367-2.972-1.367-4.997 0-6.241 4.425-13.875 9.865-17.016 1.573-.908 3.084-1.369 4.489-1.369 1.143 0 2.158.307 3.019.91a.24.24 0 0 1 .055.055c.894 1.244 1.367 2.971 1.367 4.997 0 6.241-4.425 13.875-9.865 17.016-1.573.908-3.084 1.369-4.489 1.369zm-2.718-1.172c.773.533 1.687.901 2.718.901 1.322 0 2.754-.538 4.256-1.405 5.31-3.066 9.631-10.567 9.631-16.661 0-1.908-.434-3.554-1.256-4.716-.774-.532-1.688-.814-2.718-.814-1.322 0-2.754.433-4.256 1.3-5.31 3.066-9.631 10.564-9.631 16.657 0 1.91.434 3.576 1.256 4.738z' fill='#607D8B'/><path d='M150.72 172.361l-.363-.295a24.105 24.105 0 0 0 2.148-3.128 24.05 24.05 0 0 0 1.977-4.375l.443.149a24.54 24.54 0 0 1-2.015 4.46 24.61 24.61 0 0 1-2.19 3.189M115.917 191.514l-.363-.294a24.174 24.174 0 0 0 2.148-3.128 24.038 24.038 0 0 0 1.976-4.375l.443.148a24.48 24.48 0 0 1-2.015 4.461 24.662 24.662 0 0 1-2.189 3.188M114 237.476V182.584 237.476' fill='#607D8B'/><g><path d='M81.822 37.474c.017-.135-.075-.28-.267-.392-.327-.188-.826-.21-1.109-.045l-6.012 3.471c-.131.076-.194.178-.191.285.002.132.002.461.002.578v.043l-.007.128-6.591 3.779c-.001 0-2.077 1.046-2.787 5.192 0 0-.912 6.961-.898 19.745.015 12.57.606 17.07 1.167 21.351.22 1.684 3.001 2.125 3.001 2.125.331.04.698-.027 1.08-.248l75.273-43.551c1.808-1.069 2.667-3.719 3.056-6.284 1.213-7.99 1.675-32.978-.275-39.878-.196-.693-.51-1.083-.868-1.282l-2.086-.79c-.727.028-1.416.467-1.534.535L82.032 37.072l-.21.402' fill='#FFF'/><path d='M144.311 1.701l2.085.79c.358.199.672.589.868 1.282 1.949 6.9 1.487 31.887.275 39.878-.39 2.565-1.249 5.215-3.056 6.284L69.21 93.486a1.78 1.78 0 0 1-.896.258l-.183-.011c0 .001-2.782-.44-3.003-2.124-.56-4.282-1.151-8.781-1.165-21.351-.015-12.784.897-19.745.897-19.745.71-4.146 2.787-5.192 2.787-5.192l6.591-3.779.007-.128v-.043c0-.117 0-.446-.002-.578-.003-.107.059-.21.191-.285l6.012-3.472a.98.98 0 0 1 .481-.11c.218 0 .449.053.627.156.193.112.285.258.268.392l.211-.402 60.744-34.836c.117-.068.806-.507 1.534-.535m0-.997l-.039.001c-.618.023-1.283.244-1.974.656l-.021.012-60.519 34.706a2.358 2.358 0 0 0-.831-.15c-.365 0-.704.084-.98.244l-6.012 3.471c-.442.255-.699.69-.689 1.166l.001.15-6.08 3.487c-.373.199-2.542 1.531-3.29 5.898l-.006.039c-.009.07-.92 7.173-.906 19.875.014 12.62.603 17.116 1.172 21.465l.002.015c.308 2.355 3.475 2.923 3.836 2.98l.034.004c.101.013.204.019.305.019a2.77 2.77 0 0 0 1.396-.392l75.273-43.552c1.811-1.071 2.999-3.423 3.542-6.997 1.186-7.814 1.734-33.096-.301-40.299-.253-.893-.704-1.527-1.343-1.882l-.132-.062-2.085-.789a.973.973 0 0 0-.353-.065' fill='#455A64'/><path d='M128.267 11.565l1.495.434-56.339 32.326' fill='#FFF'/><path d='M74.202 90.545a.5.5 0 0 1-.25-.931l18.437-10.645a.499.499 0 1 1 .499.864L74.451 90.478l-.249.067M75.764 42.654l-.108-.062.046-.171 5.135-2.964.17.045-.045.171-5.135 2.964-.063.017M70.52 90.375V46.421l.063-.036L137.84 7.554v43.954l-.062.036L70.52 90.375zm.25-43.811v43.38l66.821-38.579V7.985L70.77 46.564z' fill='#607D8B'/><path d='M86.986 83.182c-.23.149-.612.384-.849.523l-11.505 6.701c-.237.139-.206.252.068.252h.565c.275 0 .693-.113.93-.252L87.7 83.705c.237-.139.428-.253.425-.256a11.29 11.29 0 0 1-.006-.503c0-.274-.188-.377-.418-.227l-.715.463' fill='#607D8B'/><path d='M75.266 90.782H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.236-.138.615-.371.844-.519l.715-.464a.488.488 0 0 1 .266-.089c.172 0 .345.13.345.421 0 .214.001.363.003.437l.006.004-.004.069c-.003.075-.003.075-.486.356l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.108.866-.234l11.505-6.702c.168-.098.294-.173.361-.214-.004-.084-.004-.218-.004-.437l-.095-.171-.131.049-.714.463c-.232.15-.616.386-.854.525l-11.505 6.702-.029.018z' fill='#607D8B'/><path d='M75.266 89.871H74.7c-.2 0-.316-.056-.346-.166-.03-.11.043-.217.215-.317l11.505-6.702c.258-.151.694-.268.993-.268h.565c.2 0 .316.056.346.166.03.11-.043.217-.215.317l-11.505 6.702a2.282 2.282 0 0 1-.992.268zm-.6-.25l.034.001h.566c.252 0 .649-.107.866-.234l11.505-6.702.03-.018-.035-.001h-.565c-.252 0-.649.108-.867.234l-11.505 6.702-.029.018zM74.37 90.801v-1.247 1.247' fill='#607D8B'/><path d='M68.13 93.901c-.751-.093-1.314-.737-1.439-1.376-.831-4.238-1.151-8.782-1.165-21.352-.015-12.784.897-19.745.897-19.745.711-4.146 2.787-5.192 2.787-5.192l74.859-43.219c.223-.129 2.487-1.584 3.195.923 1.95 6.9 1.488 31.887.275 39.878-.389 2.565-1.248 5.215-3.056 6.283L69.21 93.653c-.382.221-.749.288-1.08.248 0 0-2.781-.441-3.001-2.125-.561-4.281-1.152-8.781-1.167-21.351-.014-12.784.898-19.745.898-19.745.71-4.146 2.787-5.191 2.787-5.191l6.598-3.81.871-.119 6.599-3.83.046-.461L68.13 93.901' fill='#FAFAFA'/><path d='M68.317 94.161l-.215-.013h-.001l-.244-.047c-.719-.156-2.772-.736-2.976-2.292-.568-4.34-1.154-8.813-1.168-21.384-.014-12.654.891-19.707.9-19.777.725-4.231 2.832-5.338 2.922-5.382l6.628-3.827.87-.119 6.446-3.742.034-.334a.248.248 0 0 1 .273-.223.248.248 0 0 1 .223.272l-.059.589-6.752 3.919-.87.118-6.556 3.785c-.031.016-1.99 1.068-2.666 5.018-.007.06-.908 7.086-.894 19.702.014 12.539.597 16.996 1.161 21.305.091.691.689 1.154 1.309 1.452a1.95 1.95 0 0 1-.236-.609c-.781-3.984-1.155-8.202-1.17-21.399-.014-12.653.891-19.707.9-19.777.725-4.231 2.832-5.337 2.922-5.382-.004.001 74.444-42.98 74.846-43.212l.028-.017c.904-.538 1.72-.688 2.36-.433.555.221.949.733 1.172 1.52 2.014 7.128 1.46 32.219.281 39.983-.507 3.341-1.575 5.515-3.175 6.462L69.335 93.869a2.023 2.023 0 0 1-1.018.292zm-.147-.507c.293.036.604-.037.915-.217l75.273-43.551c1.823-1.078 2.602-3.915 2.934-6.106 1.174-7.731 1.731-32.695-.268-39.772-.178-.631-.473-1.032-.876-1.192-.484-.193-1.166-.052-1.921.397l-.034.021-74.858 43.218c-.031.017-1.989 1.069-2.666 5.019-.007.059-.908 7.085-.894 19.702.015 13.155.386 17.351 1.161 21.303.09.461.476.983 1.037 1.139.114.025.185.037.196.039h.001z' fill='#455A64'/><path d='M69.317 68.982c.489-.281.885-.056.885.505 0 .56-.396 1.243-.885 1.525-.488.282-.884.057-.884-.504 0-.56.396-1.243.884-1.526' fill='#FFF'/><path d='M68.92 71.133c-.289 0-.487-.228-.487-.625 0-.56.396-1.243.884-1.526a.812.812 0 0 1 .397-.121c.289 0 .488.229.488.626 0 .56-.396 1.243-.885 1.525a.812.812 0 0 1-.397.121m.794-2.459a.976.976 0 0 0-.49.147c-.548.317-.978 1.058-.978 1.687 0 .486.271.812.674.812a.985.985 0 0 0 .491-.146c.548-.317.978-1.057.978-1.687 0-.486-.272-.813-.675-.813' fill='#8097A2'/><path d='M68.92 70.947c-.271 0-.299-.307-.299-.439 0-.491.361-1.116.79-1.363a.632.632 0 0 1 .303-.096c.272 0 .301.306.301.438 0 .491-.363 1.116-.791 1.364a.629.629 0 0 1-.304.096m.794-2.086a.812.812 0 0 0-.397.121c-.488.283-.884.966-.884 1.526 0 .397.198.625.487.625a.812.812 0 0 0 .397-.121c.489-.282.885-.965.885-1.525 0-.397-.199-.626-.488-.626' fill='#8097A2'/><path d='M69.444 85.35c.264-.152.477-.031.477.272 0 .303-.213.67-.477.822-.263.153-.477.031-.477-.271 0-.302.214-.671.477-.823' fill='#FFF'/><path d='M69.23 86.51c-.156 0-.263-.123-.263-.337 0-.302.214-.671.477-.823a.431.431 0 0 1 .214-.066c.156 0 .263.124.263.338 0 .303-.213.67-.477.822a.431.431 0 0 1-.214.066m.428-1.412c-.1 0-.203.029-.307.09-.32.185-.57.618-.57.985 0 .309.185.524.449.524a.63.63 0 0 0 .308-.09c.32-.185.57-.618.57-.985 0-.309-.185-.524-.45-.524' fill='#8097A2'/><path d='M69.23 86.322l-.076-.149c0-.235.179-.544.384-.661l.12-.041.076.151c0 .234-.179.542-.383.66l-.121.04m.428-1.038a.431.431 0 0 0-.214.066c-.263.152-.477.521-.477.823 0 .214.107.337.263.337a.431.431 0 0 0 .214-.066c.264-.152.477-.519.477-.822 0-.214-.107-.338-.263-.338' fill='#8097A2'/><path d='M139.278 7.769v43.667L72.208 90.16V46.493l67.07-38.724' fill='#455A64'/><path d='M72.083 90.375V46.421l.063-.036 67.257-38.831v43.954l-.062.036-67.258 38.831zm.25-43.811v43.38l66.821-38.579V7.985L72.333 46.564z' fill='#607D8B'/></g><path d='M125.737 88.647l-7.639 3.334V84l-11.459 4.713v8.269L99 100.315l13.369 3.646 13.368-15.314' fill='#455A64'/></g></svg>"; +function RotateInstructions() { + this.loadIcon_(); + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.top = 0; + s.right = 0; + s.bottom = 0; + s.left = 0; + s.backgroundColor = 'gray'; + s.fontFamily = 'sans-serif'; + s.zIndex = 1000000; + var img = document.createElement('img'); + img.src = this.icon; + var s = img.style; + s.marginLeft = '25%'; + s.marginTop = '25%'; + s.width = '50%'; + overlay.appendChild(img); + var text = document.createElement('div'); + var s = text.style; + s.textAlign = 'center'; + s.fontSize = '16px'; + s.lineHeight = '24px'; + s.margin = '24px 25%'; + s.width = '50%'; + text.innerHTML = 'Place your phone into your Cardboard viewer.'; + overlay.appendChild(text); + var snackbar = document.createElement('div'); + var s = snackbar.style; + s.backgroundColor = '#CFD8DC'; + s.position = 'fixed'; + s.bottom = 0; + s.width = '100%'; + s.height = '48px'; + s.padding = '14px 24px'; + s.boxSizing = 'border-box'; + s.color = '#656A6B'; + overlay.appendChild(snackbar); + var snackbarText = document.createElement('div'); + snackbarText.style.float = 'left'; + snackbarText.innerHTML = 'No Cardboard viewer?'; + var snackbarButton = document.createElement('a'); + snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; + snackbarButton.innerHTML = 'get one'; + snackbarButton.target = '_blank'; + var s = snackbarButton.style; + s.float = 'right'; + s.fontWeight = 600; + s.textTransform = 'uppercase'; + s.borderLeft = '1px solid gray'; + s.paddingLeft = '24px'; + s.textDecoration = 'none'; + s.color = '#656A6B'; + snackbar.appendChild(snackbarText); + snackbar.appendChild(snackbarButton); + this.overlay = overlay; + this.text = text; + this.hide(); +} +RotateInstructions.prototype.show = function (parent) { + if (!parent && !this.overlay.parentElement) { + document.body.appendChild(this.overlay); + } else if (parent) { + if (this.overlay.parentElement && this.overlay.parentElement != parent) this.overlay.parentElement.removeChild(this.overlay); + parent.appendChild(this.overlay); + } + this.overlay.style.display = 'block'; + var img = this.overlay.querySelector('img'); + var s = img.style; + if (isLandscapeMode()) { + s.width = '20%'; + s.marginLeft = '40%'; + s.marginTop = '3%'; + } else { + s.width = '50%'; + s.marginLeft = '25%'; + s.marginTop = '25%'; + } +}; +RotateInstructions.prototype.hide = function () { + this.overlay.style.display = 'none'; +}; +RotateInstructions.prototype.showTemporarily = function (ms, parent) { + this.show(parent); + this.timer = setTimeout(this.hide.bind(this), ms); +}; +RotateInstructions.prototype.disableShowTemporarily = function () { + clearTimeout(this.timer); +}; +RotateInstructions.prototype.update = function () { + this.disableShowTemporarily(); + if (!isLandscapeMode() && isMobile()) { + this.show(); + } else { + this.hide(); + } +}; +RotateInstructions.prototype.loadIcon_ = function () { + this.icon = dataUri('image/svg+xml', rotateInstructionsAsset); +}; +var DEFAULT_VIEWER = 'CardboardV1'; +var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; +var CLASS_NAME = 'webvr-polyfill-viewer-selector'; +function ViewerSelector(defaultViewer) { + try { + this.selectedKey = localStorage.getItem(VIEWER_KEY); + } catch (error) { + console.error('Failed to load viewer profile: %s', error); + } + if (!this.selectedKey) { + this.selectedKey = defaultViewer || DEFAULT_VIEWER; + } + this.dialog = this.createDialog_(DeviceInfo.Viewers); + this.root = null; + this.onChangeCallbacks_ = []; +} +ViewerSelector.prototype.show = function (root) { + this.root = root; + root.appendChild(this.dialog); + var selected = this.dialog.querySelector('#' + this.selectedKey); + selected.checked = true; + this.dialog.style.display = 'block'; +}; +ViewerSelector.prototype.hide = function () { + if (this.root && this.root.contains(this.dialog)) { + this.root.removeChild(this.dialog); + } + this.dialog.style.display = 'none'; +}; +ViewerSelector.prototype.getCurrentViewer = function () { + return DeviceInfo.Viewers[this.selectedKey]; +}; +ViewerSelector.prototype.getSelectedKey_ = function () { + var input = this.dialog.querySelector('input[name=field]:checked'); + if (input) { + return input.id; + } + return null; +}; +ViewerSelector.prototype.onChange = function (cb) { + this.onChangeCallbacks_.push(cb); +}; +ViewerSelector.prototype.fireOnChange_ = function (viewer) { + for (var i = 0; i < this.onChangeCallbacks_.length; i++) { + this.onChangeCallbacks_[i](viewer); + } +}; +ViewerSelector.prototype.onSave_ = function () { + this.selectedKey = this.getSelectedKey_(); + if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) { + console.error('ViewerSelector.onSave_: this should never happen!'); + return; + } + this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]); + try { + localStorage.setItem(VIEWER_KEY, this.selectedKey); + } catch (error) { + console.error('Failed to save viewer profile: %s', error); + } + this.hide(); +}; +ViewerSelector.prototype.createDialog_ = function (options) { + var container = document.createElement('div'); + container.classList.add(CLASS_NAME); + container.style.display = 'none'; + var overlay = document.createElement('div'); + var s = overlay.style; + s.position = 'fixed'; + s.left = 0; + s.top = 0; + s.width = '100%'; + s.height = '100%'; + s.background = 'rgba(0, 0, 0, 0.3)'; + overlay.addEventListener('click', this.hide.bind(this)); + var width = 280; + var dialog = document.createElement('div'); + var s = dialog.style; + s.boxSizing = 'border-box'; + s.position = 'fixed'; + s.top = '24px'; + s.left = '50%'; + s.marginLeft = -width / 2 + 'px'; + s.width = width + 'px'; + s.padding = '24px'; + s.overflow = 'hidden'; + s.background = '#fafafa'; + s.fontFamily = "'Roboto', sans-serif"; + s.boxShadow = '0px 5px 20px #666'; + dialog.appendChild(this.createH1_('Select your viewer')); + for (var id in options) { + dialog.appendChild(this.createChoice_(id, options[id].label)); + } + dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); + container.appendChild(overlay); + container.appendChild(dialog); + return container; +}; +ViewerSelector.prototype.createH1_ = function (name) { + var h1 = document.createElement('h1'); + var s = h1.style; + s.color = 'black'; + s.fontSize = '20px'; + s.fontWeight = 'bold'; + s.marginTop = 0; + s.marginBottom = '24px'; + h1.innerHTML = name; + return h1; +}; +ViewerSelector.prototype.createChoice_ = function (id, name) { + var div = document.createElement('div'); + div.style.marginTop = '8px'; + div.style.color = 'black'; + var input = document.createElement('input'); + input.style.fontSize = '30px'; + input.setAttribute('id', id); + input.setAttribute('type', 'radio'); + input.setAttribute('value', id); + input.setAttribute('name', 'field'); + var label = document.createElement('label'); + label.style.marginLeft = '4px'; + label.setAttribute('for', id); + label.innerHTML = name; + div.appendChild(input); + div.appendChild(label); + return div; +}; +ViewerSelector.prototype.createButton_ = function (label, onclick) { + var button = document.createElement('button'); + button.innerHTML = label; + var s = button.style; + s.float = 'right'; + s.textTransform = 'uppercase'; + s.color = '#1094f7'; + s.fontSize = '14px'; + s.letterSpacing = 0; + s.border = 0; + s.background = 'none'; + s.marginTop = '16px'; + button.addEventListener('click', onclick); + return button; +}; +var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {}; +function unwrapExports$$1 (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} +function createCommonjsModule$$1(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; +} +var NoSleep = createCommonjsModule$$1(function (module, exports) { +(function webpackUniversalModuleDefinition(root, factory) { + module.exports = factory(); +})(commonjsGlobal$$1, function() { +return (function(modules) { + var installedModules = {}; + function __webpack_require__(moduleId) { + if(installedModules[moduleId]) { + return installedModules[moduleId].exports; + } + var module = installedModules[moduleId] = { + i: moduleId, + l: false, + exports: {} + }; + modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + module.l = true; + return module.exports; + } + __webpack_require__.m = modules; + __webpack_require__.c = installedModules; + __webpack_require__.d = function(exports, name, getter) { + if(!__webpack_require__.o(exports, name)) { + Object.defineProperty(exports, name, { + configurable: false, + enumerable: true, + get: getter + }); + } + }; + __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; + }; + __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + __webpack_require__.p = ""; + return __webpack_require__(__webpack_require__.s = 0); + }) + ([ + (function(module, exports, __webpack_require__) { +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"); } } +var mediaFile = __webpack_require__(1); +var oldIOS = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window.MSStream; +var NoSleep = function () { + function NoSleep() { + _classCallCheck(this, NoSleep); + if (oldIOS) { + this.noSleepTimer = null; + } else { + this.noSleepVideo = document.createElement('video'); + this.noSleepVideo.setAttribute('playsinline', ''); + this.noSleepVideo.setAttribute('src', mediaFile); + this.noSleepVideo.addEventListener('timeupdate', function (e) { + if (this.noSleepVideo.currentTime > 0.5) { + this.noSleepVideo.currentTime = Math.random(); + } + }.bind(this)); + } + } + _createClass(NoSleep, [{ + key: 'enable', + value: function enable() { + if (oldIOS) { + this.disable(); + this.noSleepTimer = window.setInterval(function () { + window.location.href = '/'; + window.setTimeout(window.stop, 0); + }, 15000); + } else { + this.noSleepVideo.play(); + } + } + }, { + key: 'disable', + value: function disable() { + if (oldIOS) { + if (this.noSleepTimer) { + window.clearInterval(this.noSleepTimer); + this.noSleepTimer = null; + } + } else { + this.noSleepVideo.pause(); + } + } + }]); + return NoSleep; +}(); +module.exports = NoSleep; + }), + (function(module, exports, __webpack_require__) { +module.exports = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA='; + }) + ]); +}); +}); +var NoSleep$1 = unwrapExports$$1(NoSleep); +var nextDisplayId = 1000; +var defaultLeftBounds = [0, 0, 0.5, 1]; +var defaultRightBounds = [0.5, 0, 0.5, 1]; +var raf = window.requestAnimationFrame; +var caf = window.cancelAnimationFrame; +function VRFrameData() { + this.leftProjectionMatrix = new Float32Array(16); + this.leftViewMatrix = new Float32Array(16); + this.rightProjectionMatrix = new Float32Array(16); + this.rightViewMatrix = new Float32Array(16); + this.pose = null; +} +function VRDisplayCapabilities(config) { + Object.defineProperties(this, { + hasPosition: { + writable: false, enumerable: true, value: config.hasPosition + }, + hasExternalDisplay: { + writable: false, enumerable: true, value: config.hasExternalDisplay + }, + canPresent: { + writable: false, enumerable: true, value: config.canPresent + }, + maxLayers: { + writable: false, enumerable: true, value: config.maxLayers + }, + hasOrientation: { + enumerable: true, get: function get() { + deprecateWarning('VRDisplayCapabilities.prototype.hasOrientation', 'VRDisplay.prototype.getFrameData'); + return config.hasOrientation; + } + } + }); +} +function VRDisplay(config) { + config = config || {}; + var USE_WAKELOCK = 'wakelock' in config ? config.wakelock : true; + this.isPolyfilled = true; + this.displayId = nextDisplayId++; + this.displayName = ''; + this.depthNear = 0.01; + this.depthFar = 10000.0; + this.isPresenting = false; + Object.defineProperty(this, 'isConnected', { + get: function get() { + deprecateWarning('VRDisplay.prototype.isConnected', 'VRDisplayCapabilities.prototype.hasExternalDisplay'); + return false; + } + }); + this.capabilities = new VRDisplayCapabilities({ + hasPosition: false, + hasOrientation: false, + hasExternalDisplay: false, + canPresent: false, + maxLayers: 1 + }); + this.stageParameters = null; + this.waitingForPresent_ = false; + this.layer_ = null; + this.originalParent_ = null; + this.fullscreenElement_ = null; + this.fullscreenWrapper_ = null; + this.fullscreenElementCachedStyle_ = null; + this.fullscreenEventTarget_ = null; + this.fullscreenChangeHandler_ = null; + this.fullscreenErrorHandler_ = null; + if (USE_WAKELOCK && isMobile()) { + this.wakelock_ = new NoSleep$1(); + } +} +VRDisplay.prototype.getFrameData = function (frameData) { + return frameDataFromPose(frameData, this._getPose(), this); +}; +VRDisplay.prototype.getPose = function () { + deprecateWarning('VRDisplay.prototype.getPose', 'VRDisplay.prototype.getFrameData'); + return this._getPose(); +}; +VRDisplay.prototype.resetPose = function () { + deprecateWarning('VRDisplay.prototype.resetPose'); + return this._resetPose(); +}; +VRDisplay.prototype.getImmediatePose = function () { + deprecateWarning('VRDisplay.prototype.getImmediatePose', 'VRDisplay.prototype.getFrameData'); + return this._getPose(); +}; +VRDisplay.prototype.requestAnimationFrame = function (callback) { + return raf(callback); +}; +VRDisplay.prototype.cancelAnimationFrame = function (id) { + return caf(id); +}; +VRDisplay.prototype.wrapForFullscreen = function (element) { + if (isIOS()) { + return element; + } + if (!this.fullscreenWrapper_) { + this.fullscreenWrapper_ = document.createElement('div'); + var cssProperties = ['height: ' + Math.min(screen.height, screen.width) + 'px !important', 'top: 0 !important', 'left: 0 !important', 'right: 0 !important', 'border: 0', 'margin: 0', 'padding: 0', 'z-index: 999999 !important', 'position: fixed']; + this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';'); + this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper'); + } + if (this.fullscreenElement_ == element) { + return this.fullscreenWrapper_; + } + if (this.fullscreenElement_) { + if (this.originalParent_) { + this.originalParent_.appendChild(this.fullscreenElement_); + } else { + this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_); + } + } + this.fullscreenElement_ = element; + this.originalParent_ = element.parentElement; + if (!this.originalParent_) { + document.body.appendChild(element); + } + if (!this.fullscreenWrapper_.parentElement) { + var parent = this.fullscreenElement_.parentElement; + parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_); + parent.removeChild(this.fullscreenElement_); + } + this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild); + this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style'); + var self = this; + function applyFullscreenElementStyle() { + if (!self.fullscreenElement_) { + return; + } + var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: ' + Math.max(screen.width, screen.height) + 'px', 'height: ' + Math.min(screen.height, screen.width) + 'px', 'border: 0', 'margin: 0', 'padding: 0']; + self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';'); + } + applyFullscreenElementStyle(); + return this.fullscreenWrapper_; +}; +VRDisplay.prototype.removeFullscreenWrapper = function () { + if (!this.fullscreenElement_) { + return; + } + var element = this.fullscreenElement_; + if (this.fullscreenElementCachedStyle_) { + element.setAttribute('style', this.fullscreenElementCachedStyle_); + } else { + element.removeAttribute('style'); + } + this.fullscreenElement_ = null; + this.fullscreenElementCachedStyle_ = null; + var parent = this.fullscreenWrapper_.parentElement; + this.fullscreenWrapper_.removeChild(element); + if (this.originalParent_ === parent) { + parent.insertBefore(element, this.fullscreenWrapper_); + } + else if (this.originalParent_) { + this.originalParent_.appendChild(element); + } + parent.removeChild(this.fullscreenWrapper_); + return element; +}; +VRDisplay.prototype.requestPresent = function (layers) { + var wasPresenting = this.isPresenting; + var self = this; + if (!(layers instanceof Array)) { + deprecateWarning('VRDisplay.prototype.requestPresent with non-array argument', 'an array of VRLayers as the first argument'); + layers = [layers]; + } + return new Promise(function (resolve, reject) { + if (!self.capabilities.canPresent) { + reject(new Error('VRDisplay is not capable of presenting.')); + return; + } + if (layers.length == 0 || layers.length > self.capabilities.maxLayers) { + reject(new Error('Invalid number of layers.')); + return; + } + var incomingLayer = layers[0]; + if (!incomingLayer.source) { + resolve(); + return; + } + var leftBounds = incomingLayer.leftBounds || defaultLeftBounds; + var rightBounds = incomingLayer.rightBounds || defaultRightBounds; + if (wasPresenting) { + var layer = self.layer_; + if (layer.source !== incomingLayer.source) { + layer.source = incomingLayer.source; + } + for (var i = 0; i < 4; i++) { + layer.leftBounds[i] = leftBounds[i]; + layer.rightBounds[i] = rightBounds[i]; + } + self.wrapForFullscreen(self.layer_.source); + self.updatePresent_(); + resolve(); + return; + } + self.layer_ = { + predistorted: incomingLayer.predistorted, + source: incomingLayer.source, + leftBounds: leftBounds.slice(0), + rightBounds: rightBounds.slice(0) + }; + self.waitingForPresent_ = false; + if (self.layer_ && self.layer_.source) { + var fullscreenElement = self.wrapForFullscreen(self.layer_.source); + var onFullscreenChange = function onFullscreenChange() { + var actualFullscreenElement = getFullscreenElement(); + self.isPresenting = fullscreenElement === actualFullscreenElement; + if (self.isPresenting) { + if (screen.orientation && screen.orientation.lock) { + screen.orientation.lock('landscape-primary').catch(function (error) { + console.error('screen.orientation.lock() failed due to', error.message); + }); + } + self.waitingForPresent_ = false; + self.beginPresent_(); + resolve(); + } else { + if (screen.orientation && screen.orientation.unlock) { + screen.orientation.unlock(); + } + self.removeFullscreenWrapper(); + self.disableWakeLock(); + self.endPresent_(); + self.removeFullscreenListeners_(); + } + self.fireVRDisplayPresentChange_(); + }; + var onFullscreenError = function onFullscreenError() { + if (!self.waitingForPresent_) { + return; + } + self.removeFullscreenWrapper(); + self.removeFullscreenListeners_(); + self.disableWakeLock(); + self.waitingForPresent_ = false; + self.isPresenting = false; + reject(new Error('Unable to present.')); + }; + self.addFullscreenListeners_(fullscreenElement, onFullscreenChange, onFullscreenError); + if (requestFullscreen(fullscreenElement)) { + self.enableWakeLock(); + self.waitingForPresent_ = true; + } else if (isIOS() || isWebViewAndroid()) { + self.enableWakeLock(); + self.isPresenting = true; + self.beginPresent_(); + self.fireVRDisplayPresentChange_(); + resolve(); + } + } + if (!self.waitingForPresent_ && !isIOS()) { + exitFullscreen(); + reject(new Error('Unable to present.')); + } + }); +}; +VRDisplay.prototype.exitPresent = function () { + var wasPresenting = this.isPresenting; + var self = this; + this.isPresenting = false; + this.layer_ = null; + this.disableWakeLock(); + return new Promise(function (resolve, reject) { + if (wasPresenting) { + if (!exitFullscreen() && isIOS()) { + self.endPresent_(); + self.fireVRDisplayPresentChange_(); + } + if (isWebViewAndroid()) { + self.removeFullscreenWrapper(); + self.removeFullscreenListeners_(); + self.endPresent_(); + self.fireVRDisplayPresentChange_(); + } + resolve(); + } else { + reject(new Error('Was not presenting to VRDisplay.')); + } + }); +}; +VRDisplay.prototype.getLayers = function () { + if (this.layer_) { + return [this.layer_]; + } + return []; +}; +VRDisplay.prototype.fireVRDisplayPresentChange_ = function () { + var event = new CustomEvent('vrdisplaypresentchange', { detail: { display: this } }); + window.dispatchEvent(event); +}; +VRDisplay.prototype.fireVRDisplayConnect_ = function () { + var event = new CustomEvent('vrdisplayconnect', { detail: { display: this } }); + window.dispatchEvent(event); +}; +VRDisplay.prototype.addFullscreenListeners_ = function (element, changeHandler, errorHandler) { + this.removeFullscreenListeners_(); + this.fullscreenEventTarget_ = element; + this.fullscreenChangeHandler_ = changeHandler; + this.fullscreenErrorHandler_ = errorHandler; + if (changeHandler) { + if (document.fullscreenEnabled) { + element.addEventListener('fullscreenchange', changeHandler, false); + } else if (document.webkitFullscreenEnabled) { + element.addEventListener('webkitfullscreenchange', changeHandler, false); + } else if (document.mozFullScreenEnabled) { + document.addEventListener('mozfullscreenchange', changeHandler, false); + } else if (document.msFullscreenEnabled) { + element.addEventListener('msfullscreenchange', changeHandler, false); + } + } + if (errorHandler) { + if (document.fullscreenEnabled) { + element.addEventListener('fullscreenerror', errorHandler, false); + } else if (document.webkitFullscreenEnabled) { + element.addEventListener('webkitfullscreenerror', errorHandler, false); + } else if (document.mozFullScreenEnabled) { + document.addEventListener('mozfullscreenerror', errorHandler, false); + } else if (document.msFullscreenEnabled) { + element.addEventListener('msfullscreenerror', errorHandler, false); + } + } +}; +VRDisplay.prototype.removeFullscreenListeners_ = function () { + if (!this.fullscreenEventTarget_) return; + var element = this.fullscreenEventTarget_; + if (this.fullscreenChangeHandler_) { + var changeHandler = this.fullscreenChangeHandler_; + element.removeEventListener('fullscreenchange', changeHandler, false); + element.removeEventListener('webkitfullscreenchange', changeHandler, false); + document.removeEventListener('mozfullscreenchange', changeHandler, false); + element.removeEventListener('msfullscreenchange', changeHandler, false); + } + if (this.fullscreenErrorHandler_) { + var errorHandler = this.fullscreenErrorHandler_; + element.removeEventListener('fullscreenerror', errorHandler, false); + element.removeEventListener('webkitfullscreenerror', errorHandler, false); + document.removeEventListener('mozfullscreenerror', errorHandler, false); + element.removeEventListener('msfullscreenerror', errorHandler, false); + } + this.fullscreenEventTarget_ = null; + this.fullscreenChangeHandler_ = null; + this.fullscreenErrorHandler_ = null; +}; +VRDisplay.prototype.enableWakeLock = function () { + if (this.wakelock_) { + this.wakelock_.enable(); + } +}; +VRDisplay.prototype.disableWakeLock = function () { + if (this.wakelock_) { + this.wakelock_.disable(); + } +}; +VRDisplay.prototype.beginPresent_ = function () { +}; +VRDisplay.prototype.endPresent_ = function () { +}; +VRDisplay.prototype.submitFrame = function (pose) { +}; +VRDisplay.prototype.getEyeParameters = function (whichEye) { + return null; +}; +var config = { + ADDITIONAL_VIEWERS: [], + DEFAULT_VIEWER: '', + MOBILE_WAKE_LOCK: true, + DEBUG: false, + DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json', + K_FILTER: 0.98, + PREDICTION_TIME_S: 0.040, + CARDBOARD_UI_DISABLED: false, + ROTATE_INSTRUCTIONS_DISABLED: false, + YAW_ONLY: false, + BUFFER_SCALE: 0.5, + DIRTY_SUBMIT_FRAME_BINDINGS: false +}; +var Eye = { + LEFT: 'left', + RIGHT: 'right' +}; +function CardboardVRDisplay(config$$1) { + var defaults = extend({}, config); + config$$1 = extend(defaults, config$$1 || {}); + VRDisplay.call(this, { + wakelock: config$$1.MOBILE_WAKE_LOCK + }); + this.config = config$$1; + this.displayName = 'Cardboard VRDisplay'; + this.capabilities = new VRDisplayCapabilities({ + hasPosition: false, + hasOrientation: true, + hasExternalDisplay: false, + canPresent: true, + maxLayers: 1 + }); + this.stageParameters = null; + this.bufferScale_ = this.config.BUFFER_SCALE; + this.poseSensor_ = new PoseSensor(this.config); + this.distorter_ = null; + this.cardboardUI_ = null; + this.dpdb_ = new Dpdb(this.config.DPDB_URL, this.onDeviceParamsUpdated_.bind(this)); + this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams(), config$$1.ADDITIONAL_VIEWERS); + this.viewerSelector_ = new ViewerSelector(config$$1.DEFAULT_VIEWER); + this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)); + this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()); + if (!this.config.ROTATE_INSTRUCTIONS_DISABLED) { + this.rotateInstructions_ = new RotateInstructions(); + } + if (isIOS()) { + window.addEventListener('resize', this.onResize_.bind(this)); + } +} +CardboardVRDisplay.prototype = Object.create(VRDisplay.prototype); +CardboardVRDisplay.prototype._getPose = function () { + return { + position: null, + orientation: this.poseSensor_.getOrientation(), + linearVelocity: null, + linearAcceleration: null, + angularVelocity: null, + angularAcceleration: null + }; +}; +CardboardVRDisplay.prototype._resetPose = function () { + if (this.poseSensor_.resetPose) { + this.poseSensor_.resetPose(); + } +}; +CardboardVRDisplay.prototype._getFieldOfView = function (whichEye) { + var fieldOfView; + if (whichEye == Eye.LEFT) { + fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye(); + } else if (whichEye == Eye.RIGHT) { + fieldOfView = this.deviceInfo_.getFieldOfViewRightEye(); + } else { + console.error('Invalid eye provided: %s', whichEye); + return null; + } + return fieldOfView; +}; +CardboardVRDisplay.prototype._getEyeOffset = function (whichEye) { + var offset; + if (whichEye == Eye.LEFT) { + offset = [-this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; + } else if (whichEye == Eye.RIGHT) { + offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; + } else { + console.error('Invalid eye provided: %s', whichEye); + return null; + } + return offset; +}; +CardboardVRDisplay.prototype.getEyeParameters = function (whichEye) { + var offset = this._getEyeOffset(whichEye); + var fieldOfView = this._getFieldOfView(whichEye); + var eyeParams = { + offset: offset, + renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_, + renderHeight: this.deviceInfo_.device.height * this.bufferScale_ + }; + Object.defineProperty(eyeParams, 'fieldOfView', { + enumerable: true, + get: function get() { + deprecateWarning('VRFieldOfView', 'VRFrameData\'s projection matrices'); + return fieldOfView; + } + }); + return eyeParams; +}; +CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function (newParams) { + if (this.config.DEBUG) { + console.log('DPDB reported that device params were updated.'); + } + this.deviceInfo_.updateDeviceParams(newParams); + if (this.distorter_) { + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } +}; +CardboardVRDisplay.prototype.updateBounds_ = function () { + if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) { + this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds); + } +}; +CardboardVRDisplay.prototype.beginPresent_ = function () { + var gl = this.layer_.source.getContext('webgl'); + if (!gl) gl = this.layer_.source.getContext('experimental-webgl'); + if (!gl) gl = this.layer_.source.getContext('webgl2'); + if (!gl) return; + if (this.layer_.predistorted) { + if (!this.config.CARDBOARD_UI_DISABLED) { + gl.canvas.width = getScreenWidth() * this.bufferScale_; + gl.canvas.height = getScreenHeight() * this.bufferScale_; + this.cardboardUI_ = new CardboardUI(gl); + } + } else { + if (!this.config.CARDBOARD_UI_DISABLED) { + this.cardboardUI_ = new CardboardUI(gl); + } + this.distorter_ = new CardboardDistorter(gl, this.cardboardUI_, this.config.BUFFER_SCALE, this.config.DIRTY_SUBMIT_FRAME_BINDINGS); + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } + if (this.cardboardUI_) { + this.cardboardUI_.listen(function (e) { + this.viewerSelector_.show(this.layer_.source.parentElement); + e.stopPropagation(); + e.preventDefault(); + }.bind(this), function (e) { + this.exitPresent(); + e.stopPropagation(); + e.preventDefault(); + }.bind(this)); + } + if (this.rotateInstructions_) { + if (isLandscapeMode() && isMobile()) { + this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement); + } else { + this.rotateInstructions_.update(); + } + } + this.orientationHandler = this.onOrientationChange_.bind(this); + window.addEventListener('orientationchange', this.orientationHandler); + this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this); + window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); + this.fireVRDisplayDeviceParamsChange_(); +}; +CardboardVRDisplay.prototype.endPresent_ = function () { + if (this.distorter_) { + this.distorter_.destroy(); + this.distorter_ = null; + } + if (this.cardboardUI_) { + this.cardboardUI_.destroy(); + this.cardboardUI_ = null; + } + if (this.rotateInstructions_) { + this.rotateInstructions_.hide(); + } + this.viewerSelector_.hide(); + window.removeEventListener('orientationchange', this.orientationHandler); + window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); +}; +CardboardVRDisplay.prototype.updatePresent_ = function () { + this.endPresent_(); + this.beginPresent_(); +}; +CardboardVRDisplay.prototype.submitFrame = function (pose) { + if (this.distorter_) { + this.updateBounds_(); + this.distorter_.submitFrame(); + } else if (this.cardboardUI_ && this.layer_) { + var canvas = this.layer_.source.getContext('webgl').canvas; + if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) { + this.cardboardUI_.onResize(); + } + this.lastWidth = canvas.width; + this.lastHeight = canvas.height; + this.cardboardUI_.render(); + } +}; +CardboardVRDisplay.prototype.onOrientationChange_ = function (e) { + this.viewerSelector_.hide(); + if (this.rotateInstructions_) { + this.rotateInstructions_.update(); + } + this.onResize_(); +}; +CardboardVRDisplay.prototype.onResize_ = function (e) { + if (this.layer_) { + var gl = this.layer_.source.getContext('webgl'); + var cssProperties = ['position: absolute', 'top: 0', 'left: 0', + 'width: 100vw', 'height: 100vh', 'border: 0', 'margin: 0', + 'padding: 0px', 'box-sizing: content-box']; + gl.canvas.setAttribute('style', cssProperties.join('; ') + ';'); + safariCssSizeWorkaround(gl.canvas); + } +}; +CardboardVRDisplay.prototype.onViewerChanged_ = function (viewer) { + this.deviceInfo_.setViewer(viewer); + if (this.distorter_) { + this.distorter_.updateDeviceInfo(this.deviceInfo_); + } + this.fireVRDisplayDeviceParamsChange_(); +}; +CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function () { + var event = new CustomEvent('vrdisplaydeviceparamschange', { + detail: { + vrdisplay: this, + deviceInfo: this.deviceInfo_ + } + }); + window.dispatchEvent(event); +}; +CardboardVRDisplay.VRFrameData = VRFrameData; +CardboardVRDisplay.VRDisplay = VRDisplay; +return CardboardVRDisplay; +}))); +}); +var CardboardVRDisplay = unwrapExports(cardboardVrDisplay); + +class XRDevice extends EventTarget { + constructor(global) { + super(); + this.global = global; + this.onWindowResize = this.onWindowResize.bind(this); + this.global.window.addEventListener('resize', this.onWindowResize); + this.environmentBlendMode = 'opaque'; + } + get depthNear() { throw new Error('Not implemented'); } + set depthNear(val) { throw new Error('Not implemented'); } + get depthFar() { throw new Error('Not implemented'); } + set depthFar(val) { throw new Error('Not implemented'); } + onBaseLayerSet(sessionId, layer) { throw new Error('Not implemented'); } + onInlineVerticalFieldOfViewSet(sessionId, value) { throw new Error('Not implemented'); } + supportsSession(mode) { throw new Error('Not implemented'); } + async requestSession(mode) { throw new Error('Not implemented'); } + requestAnimationFrame(callback) { throw new Error('Not implemented'); } + onFrameStart(sessionId) { throw new Error('Not implemented'); } + onFrameEnd(sessionId) { throw new Error('Not implemented'); } + requestStageBounds() { throw new Error('Not implemented'); } + async requestFrameOfReferenceTransform(type, options) { + return undefined; + } + cancelAnimationFrame(handle) { throw new Error('Not implemented'); } + endSession(sessionId) { throw new Error('Not implemented'); } + getViewport(sessionId, eye, layer, target) { throw new Error('Not implemented'); } + getProjectionMatrix(eye) { throw new Error('Not implemented'); } + getBasePoseMatrix() { throw new Error('Not implemented'); } + getBaseViewMatrix(eye) { throw new Error('Not implemented'); } + getInputSources() { throw new Error('Not implemented'); } + getInputPose(inputSource, coordinateSystem, poseType) { throw new Error('Not implemented'); } + onWindowResize() { + this.onWindowResize(); + } +} + +let oculusTouch = { + mapping: 'xr-standard', + id: 'oculus-touch', + buttons: { + length: 6, + 0: 1, + 1: 0, + 2: 2, + 3: null, + 4: 3, + 5: 4 + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] + } +}; +let windowsMixedReality = { + mapping: 'xr-standard', + id: 'windows-mixed-reality', + buttons: { + length: 4, + 0: 1, + 1: 0, + 2: 2, + 3: 4, + }, + gripTransform: { + position: [0, -0.02, 0.04, 1], + orientation: [Math.PI * 0.11, 0, 0, 1] + } +}; +let GamepadMappings = { + "Oculus Touch (Right)": oculusTouch, + "Oculus Touch (Left)": oculusTouch, + "Oculus Go Controller": { + mapping: 'xr-standard', + id: 'oculus-go', + buttons: { + 0: 1, + 1: 0, + }, + gripTransform: { + orientation: [Math.PI * 0.11, 0, 0, 1] + } + }, + "Windows Mixed Reality (Right)": windowsMixedReality, + "Windows Mixed Reality (Left)": windowsMixedReality, +}; + +const HEAD_ELBOW_OFFSET_RIGHTHANDED = fromValues$1(0.155, -0.465, -0.15); +const HEAD_ELBOW_OFFSET_LEFTHANDED = fromValues$1(-0.155, -0.465, -0.15); +const ELBOW_WRIST_OFFSET = fromValues$1(0, 0, -0.25); +const WRIST_CONTROLLER_OFFSET = fromValues$1(0, 0, 0.05); +const ARM_EXTENSION_OFFSET = fromValues$1(-0.08, 0.14, 0.08); +const ELBOW_BEND_RATIO = 0.4; +const EXTENSION_RATIO_WEIGHT = 0.4; +const MIN_ANGULAR_SPEED = 0.61; +const MIN_ANGLE_DELTA = 0.175; +const MIN_EXTENSION_COS = 0.12; +const MAX_EXTENSION_COS = 0.87; +const RAD_TO_DEG = 180 / Math.PI; +function eulerFromQuaternion(out, q, order) { + function clamp(value, min$$1, max$$1) { + return (value < min$$1 ? min$$1 : (value > max$$1 ? max$$1 : value)); + } + var sqx = q[0] * q[0]; + var sqy = q[1] * q[1]; + var sqz = q[2] * q[2]; + var sqw = q[3] * q[3]; + if ( order === 'XYZ' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[1] * q[2] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[0] * q[2] + q[1] * q[3] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YXZ' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] - q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZXY' ) { + out[0] = Math.asin( clamp( 2 * ( q[0] * q[3] + q[1] * q[2] ), -1, 1 ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[2] * q[0] ), ( sqw - sqx - sqy + sqz ) ); + out[2] = Math.atan2( 2 * ( q[2] * q[3] - q[0] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + } else if ( order === 'ZYX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[2] * q[1] ), ( sqw - sqx - sqy + sqz ) ); + out[1] = Math.asin( clamp( 2 * ( q[1] * q[3] - q[0] * q[2] ), -1, 1 ) ); + out[2] = Math.atan2( 2 * ( q[0] * q[1] + q[2] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + } else if ( order === 'YZX' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] - q[2] * q[1] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[1] * q[3] - q[0] * q[2] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[0] * q[1] + q[2] * q[3] ), -1, 1 ) ); + } else if ( order === 'XZY' ) { + out[0] = Math.atan2( 2 * ( q[0] * q[3] + q[1] * q[2] ), ( sqw - sqx + sqy - sqz ) ); + out[1] = Math.atan2( 2 * ( q[0] * q[2] + q[1] * q[3] ), ( sqw + sqx - sqy - sqz ) ); + out[2] = Math.asin( clamp( 2 * ( q[2] * q[3] - q[0] * q[1] ), -1, 1 ) ); + } else { + console.log('No order given for quaternion to euler conversion.'); + return; + } +} +class OrientationArmModel { + constructor() { + this.hand = 'right'; + this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; + this.controllerQ = create$4(); + this.lastControllerQ = create$4(); + this.headQ = create$4(); + this.headPos = create$1(); + this.elbowPos = create$1(); + this.wristPos = create$1(); + this.time = null; + this.lastTime = null; + this.rootQ = create$4(); + this.position = create$1(); + } + setHandedness(hand) { + if (this.hand != hand) { + this.hand = hand; + if (this.hand == 'left') { + this.headElbowOffset = HEAD_ELBOW_OFFSET_LEFTHANDED; + } else { + this.headElbowOffset = HEAD_ELBOW_OFFSET_RIGHTHANDED; + } + } + } + update(controllerOrientation, headPoseMatrix) { + this.time = now$1(); + if (controllerOrientation) { + copy$4(this.lastControllerQ, this.controllerQ); + copy$4(this.controllerQ, controllerOrientation); + } + if (headPoseMatrix) { + getTranslation(this.headPos, headPoseMatrix); + getRotation(this.headQ, headPoseMatrix); + } + let headYawQ = this.getHeadYawOrientation_(); + let angleDelta = this.quatAngle_(this.lastControllerQ, this.controllerQ); + let timeDelta = (this.time - this.lastTime) / 1000; + let controllerAngularSpeed = angleDelta / timeDelta; + if (controllerAngularSpeed > MIN_ANGULAR_SPEED) { + slerp(this.rootQ, this.rootQ, headYawQ, + Math.min(angleDelta / MIN_ANGLE_DELTA, 1.0)); + } else { + copy$4(this.rootQ, headYawQ); + } + let controllerForward = fromValues$1(0, 0, -1.0); + transformQuat(controllerForward, controllerForward, this.controllerQ); + let controllerDotY = dot(controllerForward, [0, 1, 0]); + let extensionRatio = this.clamp_( + (controllerDotY - MIN_EXTENSION_COS) / MAX_EXTENSION_COS, 0.0, 1.0); + let controllerCameraQ = clone$4(this.rootQ); + invert$2(controllerCameraQ, controllerCameraQ); + multiply$4(controllerCameraQ, controllerCameraQ, this.controllerQ); + let elbowPos = this.elbowPos; + copy$1(elbowPos, this.headPos); + add$1(elbowPos, elbowPos, this.headElbowOffset); + let elbowOffset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(elbowOffset, elbowOffset, extensionRatio); + add$1(elbowPos, elbowPos, elbowOffset); + let totalAngle = this.quatAngle_(controllerCameraQ, create$4()); + let totalAngleDeg = totalAngle * RAD_TO_DEG; + let lerpSuppression = 1 - Math.pow(totalAngleDeg / 180, 4);let elbowRatio = ELBOW_BEND_RATIO; + let wristRatio = 1 - ELBOW_BEND_RATIO; + let lerpValue = lerpSuppression * + (elbowRatio + wristRatio * extensionRatio * EXTENSION_RATIO_WEIGHT); + let wristQ = create$4(); + slerp(wristQ, wristQ, controllerCameraQ, lerpValue); + let invWristQ = invert$2(create$4(), wristQ); + let elbowQ = clone$4(controllerCameraQ); + multiply$4(elbowQ, elbowQ, invWristQ); + let wristPos = this.wristPos; + copy$1(wristPos, WRIST_CONTROLLER_OFFSET); + transformQuat(wristPos, wristPos, wristQ); + add$1(wristPos, wristPos, ELBOW_WRIST_OFFSET); + transformQuat(wristPos, wristPos, elbowQ); + add$1(wristPos, wristPos, elbowPos); + let offset = clone$1(ARM_EXTENSION_OFFSET); + scale$1(offset, offset, extensionRatio); + add$1(this.position, this.wristPos, offset); + transformQuat(this.position, this.position, this.rootQ); + this.lastTime = this.time; + } + getPosition() { + return this.position; + } + getHeadYawOrientation_() { + let headEuler = create$1(); + eulerFromQuaternion(headEuler, this.headQ, 'YXZ'); + let destinationQ = fromEuler(create$4(), 0, headEuler[1] * RAD_TO_DEG, 0); + return destinationQ; + } + clamp_(value, min$$1, max$$1) { + return Math.min(Math.max(value, min$$1), max$$1); + } + quatAngle_(q1, q2) { + let vec1 = [0, 0, -1]; + let vec2 = [0, 0, -1]; + transformQuat(vec1, vec1, q1); + transformQuat(vec2, vec2, q2); + return angle(vec1, vec2); + } +} + +const PRIVATE$16 = Symbol('@@webxr-polyfill/XRRemappedGamepad'); +const PLACEHOLDER_BUTTON = { pressed: false, touched: false, value: 0.0 }; +Object.freeze(PLACEHOLDER_BUTTON); +class XRRemappedGamepad { + constructor(gamepad, map) { + if (!map) { + map = {}; + } + let axes = new Array(map.axes ? map.axes.length : gamepad.axes.length); + let buttons = new Array(map.buttons ? map.buttons.length : gamepad.buttons.length); + let gripTransform = null; + if (map.gripTransform) { + let orientation = map.gripTransform.orientation || [0, 0, 0, 1]; + gripTransform = create(); + fromRotationTranslation( + gripTransform, + normalize$2(orientation, orientation), + map.gripTransform.position || [0, 0, 0] + ); + } + let targetRayTransform = null; + if (map.targetRayTransform) { + let orientation = map.targetRayTransform.orientation || [0, 0, 0, 1]; + targetRayTransform = create(); + fromRotationTranslation( + targetRayTransform, + normalize$2(orientation, orientation), + map.targetRayTransform.position || [0, 0, 0] + ); + } + this[PRIVATE$16] = { + gamepad, + map, + id: map.id || gamepad.id, + mapping: map.mapping || gamepad.mapping, + axes, + buttons, + gripTransform, + targetRayTransform, + }; + this._update(); + } + _update() { + let gamepad = this[PRIVATE$16].gamepad; + let map = this[PRIVATE$16].map; + let axes = this[PRIVATE$16].axes; + for (let i = 0; i < axes.length; ++i) { + if (map.axes && i in map.axes) { + if (map.axes[i] === null) { + axes[i] = 0; + } else { + axes[i] = gamepad.axes[map.axes[i]]; + } + } else { + axes[i] = gamepad.axes[i]; + } + } + let buttons = this[PRIVATE$16].buttons; + for (let i = 0; i < buttons.length; ++i) { + if (map.buttons && i in map.buttons) { + if (map.buttons[i] === null) { + buttons[i] = PLACEHOLDER_BUTTON; + } else { + buttons[i] = gamepad.buttons[map.buttons[i]]; + } + } else { + buttons[i] = gamepad.buttons[i]; + } + } + } + get id() { + return this[PRIVATE$16].id; + } + get index() { + return 0; + } + get connected() { + return this[PRIVATE$16].gamepad.connected; + } + get timestamp() { + return this[PRIVATE$16].gamepad.timestamp; + } + get mapping() { + return this[PRIVATE$16].mapping; + } + get axes() { + return this[PRIVATE$16].axes; + } + get buttons() { + return this[PRIVATE$16].buttons; + } +} +class GamepadXRInputSource { + constructor(polyfill, primaryButtonIndex = 0) { + this.polyfill = polyfill; + this.nativeGamepad = null; + this.gamepad = null; + this.inputSource = new XRInputSource(this); + this.lastPosition = create$1(); + this.emulatedPosition = false; + this.basePoseMatrix = create(); + this.outputMatrix = create(); + this.inputPoses = new WeakMap(); + this.primaryButtonIndex = primaryButtonIndex; + this.primaryActionPressed = false; + this.handedness = ''; + this.targetRayMode = 'gaze'; + this.armModel = null; + } + updateFromGamepad(gamepad) { + if (this.nativeGamepad !== gamepad) { + this.nativeGamepad = gamepad; + if (gamepad) { + this.gamepad = new XRRemappedGamepad(gamepad, GamepadMappings[gamepad.id]); + } else { + this.gamepad = null; + } + } + this.handedness = gamepad.hand; + if (this.gamepad) { + this.gamepad._update(); + } + if (gamepad.pose) { + this.targetRayMode = 'tracked-pointer'; + this.emulatedPosition = !gamepad.pose.hasPosition; + } else if (gamepad.hand === '') { + this.targetRayMode = 'gaze'; + this.emulatedPosition = false; + } + } + updateBasePoseMatrix() { + if (this.nativeGamepad && this.nativeGamepad.pose) { + let pose = this.nativeGamepad.pose; + let position = pose.position; + let orientation = pose.orientation; + if (!position && !orientation) { + return; + } + if (!position) { + if (!pose.hasPosition) { + if (!this.armModel) { + this.armModel = new OrientationArmModel(); + } + this.armModel.setHandedness(this.nativeGamepad.hand); + this.armModel.update(orientation, this.polyfill.getBasePoseMatrix()); + position = this.armModel.getPosition(); + } else { + position = this.lastPosition; + } + } else { + this.lastPosition[0] = position[0]; + this.lastPosition[1] = position[1]; + this.lastPosition[2] = position[2]; + } + fromRotationTranslation(this.basePoseMatrix, orientation, position); + } else { + copy(this.basePoseMatrix, this.polyfill.getBasePoseMatrix()); + } + return this.basePoseMatrix; + } + getXRPose(coordinateSystem, poseType) { + this.updateBasePoseMatrix(); + switch(poseType) { + case "target-ray": + coordinateSystem.transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$16].targetRayTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$16].targetRayTransform); + } + break; + case "grip": + if (!this.nativeGamepad || !this.nativeGamepad.pose) { + return null; + } + coordinateSystem.transformBasePoseMatrix(this.outputMatrix, this.basePoseMatrix); + if (this.gamepad && this.gamepad[PRIVATE$16].gripTransform) { + multiply(this.outputMatrix, this.outputMatrix, this.gamepad[PRIVATE$16].gripTransform); + } + break; + default: + return null; + } + coordinateSystem._adjustForOriginOffset(this.outputMatrix); + return new XRPose(new XRRigidTransform(this.outputMatrix), this.emulatedPosition); + } +} + +const TEST_ENV = "production" === 'test'; +const EXTRA_PRESENTATION_ATTRIBUTES = { + highRefreshRate: true, +}; +const PRIMARY_BUTTON_MAP = { + oculus: 1, + openvr: 1, + 'spatial controller (spatial interaction source)': 1 +}; +let SESSION_ID = 0; +class Session { + constructor(mode, polyfillOptions={}) { + this.mode = mode; + this.outputContext = null; + this.immersive = mode == 'immersive-vr' || mode == 'immersive-ar'; + this.ended = null; + this.baseLayer = null; + this.inlineVerticalFieldOfView = Math.PI * 0.5; + this.id = ++SESSION_ID; + this.modifiedCanvasLayer = false; + if (this.outputContext && !TEST_ENV) { + const renderContextType = polyfillOptions.renderContextType || '2d'; + this.renderContext = this.outputContext.canvas.getContext(renderContextType); + } + } +} +class WebVRDevice extends XRDevice { + constructor(global, display) { + const { canPresent } = display.capabilities; + super(global); + this.display = display; + this.frame = new global.VRFrameData(); + this.sessions = new Map(); + this.immersiveSession = null; + this.canPresent = canPresent; + this.baseModelMatrix = create(); + this.gamepadInputSources = {}; + this.tempVec3 = new Float32Array(3); + this.onVRDisplayPresentChange = this.onVRDisplayPresentChange.bind(this); + global.window.addEventListener('vrdisplaypresentchange', this.onVRDisplayPresentChange); + this.CAN_USE_GAMEPAD = global.navigator && ('getGamepads' in global.navigator); + this.HAS_BITMAP_SUPPORT = isImageBitmapSupported(global); + } + get depthNear() { return this.display.depthNear; } + set depthNear(val) { this.display.depthNear = val; } + get depthFar() { return this.display.depthFar; } + set depthFar(val) { this.display.depthFar = val; } + onBaseLayerSet(sessionId, layer) { + const session = this.sessions.get(sessionId); + const canvas = layer.context.canvas; + if (session.immersive) { + const left = this.display.getEyeParameters('left'); + const right = this.display.getEyeParameters('right'); + canvas.width = Math.max(left.renderWidth, right.renderWidth) * 2; + canvas.height = Math.max(left.renderHeight, right.renderHeight); + this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES + }]).then(() => { + if (!TEST_ENV && !this.global.document.body.contains(canvas)) { + session.modifiedCanvasLayer = true; + this.global.document.body.appendChild(canvas); + applyCanvasStylesForMinimalRendering(canvas); + } + session.baseLayer = layer; + }); + } + else { + session.baseLayer = layer; + } + } + onInlineVerticalFieldOfViewSet(sessionId, value) { + const session = this.sessions.get(sessionId); + session.inlineVerticalFieldOfView = value; + } + supportsSession(mode) { + if (XRSessionModes.indexOf(mode) == -1) { + throw new TypeError( + `The provided value '${mode}' is not a valid enum value of type XRSessionMode`); + } + if (mode == 'immersive-ar') { + return false; + } + if (mode == 'immersive-vr' && this.canPresent === false) { + return false; + } + return true; + } + async requestSession(mode) { + if (!this.supportsSession(mode)) { + return Promise.reject(); + } + let immersive = mode == 'immersive-vr'; + if (immersive) { + const canvas = this.global.document.createElement('canvas'); + if (!TEST_ENV) { + const ctx = canvas.getContext('webgl'); + } + await this.display.requestPresent([{ + source: canvas, attributes: EXTRA_PRESENTATION_ATTRIBUTES }]); + } + const session = new Session(mode, { + renderContextType: this.HAS_BITMAP_SUPPORT ? 'bitmaprenderer' : '2d' + }); + this.sessions.set(session.id, session); + if (immersive) { + this.immersiveSession = session; + this.dispatchEvent('@@webxr-polyfill/vr-present-start', session.id); + } + return Promise.resolve(session.id); + } + requestAnimationFrame(callback) { + return this.display.requestAnimationFrame(callback); + } + getPrimaryButtonIndex(gamepad) { + let primaryButton = 0; + let name = gamepad.id.toLowerCase(); + for (let key in PRIMARY_BUTTON_MAP) { + if (name.includes(key)) { + primaryButton = PRIMARY_BUTTON_MAP[key]; + break; + } + } + return Math.min(primaryButton, gamepad.buttons.length - 1); + } + onFrameStart(sessionId) { + this.display.getFrameData(this.frame); + const session = this.sessions.get(sessionId); + if (session.immersive && this.CAN_USE_GAMEPAD) { + let prevInputSources = this.gamepadInputSources; + this.gamepadInputSources = {}; + let gamepads = this.global.navigator.getGamepads(); + for (let i = 0; i < gamepads.length; ++i) { + let gamepad = gamepads[i]; + if (gamepad && gamepad.displayId > 0) { + let inputSourceImpl = prevInputSources[i]; + if (!inputSourceImpl) { + inputSourceImpl = new GamepadXRInputSource(this, this.getPrimaryButtonIndex(gamepad)); + } + inputSourceImpl.updateFromGamepad(gamepad); + this.gamepadInputSources[i] = inputSourceImpl; + if (inputSourceImpl.primaryButtonIndex != -1) { + let primaryActionPressed = gamepad.buttons[inputSourceImpl.primaryButtonIndex].pressed; + if (primaryActionPressed && !inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-start', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } else if (!primaryActionPressed && inputSourceImpl.primaryActionPressed) { + this.dispatchEvent('@@webxr-polyfill/input-select-end', { sessionId: session.id, inputSource: inputSourceImpl.inputSource }); + } + inputSourceImpl.primaryActionPressed = primaryActionPressed; + } + } + } + } + if (TEST_ENV) { + return; + } + if (!session.immersive && session.baseLayer) { + const canvas = session.baseLayer.context.canvas; + perspective(this.frame.leftProjectionMatrix, session.inlineVerticalFieldOfView, + canvas.width/canvas.height, this.depthNear, this.depthFar); + } + } + onFrameEnd(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended || !session.baseLayer) { + return; + } + if (session.outputContext && + !(session.immersive && !this.display.capabilities.hasExternalDisplay)) { + const mirroring = + session.immersive && this.display.capabilities.hasExternalDisplay; + const iCanvas = session.baseLayer.context.canvas; + const iWidth = mirroring ? iCanvas.width / 2 : iCanvas.width; + const iHeight = iCanvas.height; + if (!TEST_ENV) { + const oCanvas = session.outputContext.canvas; + const oWidth = oCanvas.width; + const oHeight = oCanvas.height; + const renderContext = session.renderContext; + if (this.HAS_BITMAP_SUPPORT) { + if (iCanvas.transferToImageBitmap) { + renderContext.transferFromImageBitmap(iCanvas.transferToImageBitmap()); + } + else { + this.global.createImageBitmap(iCanvas, 0, 0, iWidth, iHeight, { + resizeWidth: oWidth, + resizeHeight: oHeight, + }).then(bitmap => renderContext.transferFromImageBitmap(bitmap)); + } + } else { + renderContext.drawImage(iCanvas, 0, 0, iWidth, iHeight, + 0, 0, oWidth, oHeight); + } + } + } + if (session.immersive && session.baseLayer) { + this.display.submitFrame(); + } + } + cancelAnimationFrame(handle) { + this.display.cancelAnimationFrame(handle); + } + async endSession(sessionId) { + const session = this.sessions.get(sessionId); + if (session.ended) { + return; + } + if (session.immersive) { + return this.display.exitPresent(); + } else { + session.ended = true; + } + } + requestStageBounds() { + if (this.display.stageParameters) { + const width = this.display.stageParameters.sizeX; + const depth = this.display.stageParameters.sizeZ; + const data = []; + data.push(-width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(-depth / 2); + data.push(width / 2); + data.push(depth / 2); + data.push(-width / 2); + data.push(depth / 2); + return data; + } + return null; + } + async requestFrameOfReferenceTransform(type, options) { + if (type === 'stage' && this.display.stageParameters && + this.display.stageParameters.sittingToStandingTransform) { + return this.display.stageParameters.sittingToStandingTransform; + } + } + getProjectionMatrix(eye) { + if (eye === 'left') { + return this.frame.leftProjectionMatrix; + } else if (eye === 'right') { + return this.frame.rightProjectionMatrix; + } else if (eye === 'none') { + return this.frame.leftProjectionMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); + } + } + getViewport(sessionId, eye, layer, target) { + const session = this.sessions.get(sessionId); + const { width, height } = layer.context.canvas; + if (!session.immersive) { + target.x = target.y = 0; + target.width = width; + target.height = height; + return true; + } + if (eye === 'left') { + target.x = 0; + } else if (eye === 'right') { + target.x = width / 2; + } else { + return false; + } + target.y = 0; + target.width = width / 2; + target.height = height; + return true; + } + getBasePoseMatrix() { + let { position, orientation } = this.frame.pose; + if (!position && !orientation) { + return this.baseModelMatrix; + } + if (!position) { + position = this.tempVec3; + position[0] = position[1] = position[2] = 0; + } + fromRotationTranslation(this.baseModelMatrix, orientation, position); + return this.baseModelMatrix; + } + getBaseViewMatrix(eye) { + if (eye === 'left') { + return this.frame.leftViewMatrix; + } else if (eye === 'right') { + return this.frame.rightViewMatrix; + } else { + throw new Error(`eye must be of type 'left' or 'right'`); + } + } + getInputSources() { + let inputSources = []; + for (let i in this.gamepadInputSources) { + inputSources.push(this.gamepadInputSources[i].inputSource); + } + return inputSources; + } + getInputPose(inputSource, coordinateSystem, poseType) { + if (!coordinateSystem) { + return null; + } + for (let i in this.gamepadInputSources) { + let inputSourceImpl = this.gamepadInputSources[i]; + if (inputSourceImpl.inputSource === inputSource) { + return inputSourceImpl.getXRPose(coordinateSystem, poseType); + } + } + return null; + } + onWindowResize() { + } + onVRDisplayPresentChange(e) { + if (!this.display.isPresenting) { + this.sessions.forEach(session => { + if (session.immersive && !session.ended) { + if (session.modifiedCanvasLayer) { + const canvas = session.baseLayer.context.canvas; + document.body.removeChild(canvas); + canvas.setAttribute('style', ''); + } + if (this.immersiveSession === session) { + this.immersiveSession = null; + } + this.dispatchEvent('@@webxr-polyfill/vr-present-end', session.id); + } + }); + } + } +} + +class CardboardXRDevice extends WebVRDevice { + constructor(global, cardboardConfig) { + const display = new CardboardVRDisplay(cardboardConfig || {}); + super(global, display); + this.display = display; + this.frame = { + rightViewMatrix: new Float32Array(16), + leftViewMatrix: new Float32Array(16), + rightProjectionMatrix: new Float32Array(16), + leftProjectionMatrix: new Float32Array(16), + pose: null, + timestamp: null, + }; + } +} + +const getWebVRDevice = async function (global) { + let device = null; + if ('getVRDisplays' in global.navigator) { + try { + const displays = await global.navigator.getVRDisplays(); + if (displays && displays.length) { + device = new WebVRDevice(global, displays[0]); + } + } catch (e) {} + } + return device; +}; +const requestXRDevice = async function (global, config) { + if (config.webvr) { + let xr = await getWebVRDevice(global); + if (xr) { + return xr; + } + } + if (!global.VRFrameData) { + global.VRFrameData = function () { + this.rightViewMatrix = new Float32Array(16); + this.leftViewMatrix = new Float32Array(16); + this.rightProjectionMatrix = new Float32Array(16); + this.leftProjectionMatrix = new Float32Array(16); + this.pose = null; + }; + } + return new CardboardXRDevice(global, config.cardboardConfig); +}; + +const CONFIG_DEFAULTS = { + global: _global, + webvr: true, + cardboard: true, + cardboardConfig: null, + allowCardboardOnDesktop: false, +}; +const partials = ['navigator', 'HTMLCanvasElement', 'WebGLRenderingContext']; +class WebXRPolyfill { + constructor(config={}) { + this.config = Object.freeze(Object.assign({}, CONFIG_DEFAULTS, config)); + this.global = this.config.global; + this.nativeWebXR = 'xr' in this.global.navigator; + this.injected = false; + if (!this.nativeWebXR) { + this._injectPolyfill(this.global); + } + else if (this.config.cardboard && isMobile(this.global)) { + this._patchNavigatorXR(); + } + } + _injectPolyfill(global) { + if (!partials.every(iface => !!global[iface])) { + throw new Error(`Global must have the following attributes : ${partials}`); + } + for (const className of Object.keys(API)) { + if (global[className] !== undefined) { + console.warn(`${className} already defined on global.`); + } else { + global[className] = API[className]; + } + } + { + const polyfilledCtx = polyfillMakeXRCompatible(global.WebGLRenderingContext); + if (polyfilledCtx) { + polyfillGetContext(global.HTMLCanvasElement); + if (global.OffscreenCanvas) { + polyfillGetContext(global.OffscreenCanvas); + } + if (global.WebGL2RenderingContext){ + polyfillMakeXRCompatible(global.WebGL2RenderingContext); + } + } + } + this.injected = true; + this._patchNavigatorXR(); + } + _patchNavigatorXR() { + let devicePromise = requestXRDevice(this.global, this.config); + this.xr = new XR(devicePromise); + Object.defineProperty(this.global.navigator, 'xr', { + value: this.xr, + configurable: true, + }); + } +} + +export default WebXRPolyfill;
diff --git a/third_party/zlib/chromeconf.h b/third_party/zlib/chromeconf.h index 3414831..e9aa386 100644 --- a/third_party/zlib/chromeconf.h +++ b/third_party/zlib/chromeconf.h
@@ -158,7 +158,7 @@ #define voidpc Cr_z_voidpc #define voidpf Cr_z_voidpf #define gz_header_s Cr_z_gz_header_s -#define internal_state Cr_z_internal_state +/* #undef internal_state */ /* #undef z_off64_t */ /* An exported symbol that isn't handled by Z_PREFIX in zconf.h */
diff --git a/third_party/zlib/patches/README b/third_party/zlib/patches/README index 4cf06b81..b21d309f 100644 --- a/third_party/zlib/patches/README +++ b/third_party/zlib/patches/README
@@ -4,6 +4,7 @@ build. - 0001-simd.patch: integrate Intel SIMD optimizations from https://github.com/jtkukunas/zlib/ + - 0002-uninitializedcheck.patch: prevent uninitialized use of state->check == Procedure to create a patch file ==
diff --git a/tools/cfi/blacklist.txt b/tools/cfi/blacklist.txt index 0adc976..c2199d1 100644 --- a/tools/cfi/blacklist.txt +++ b/tools/cfi/blacklist.txt
@@ -185,6 +185,9 @@ # Call to libcurl.so from the symupload utility src:*third_party/breakpad/breakpad/src/common/linux/http_upload.cc +# Indirect call to Xlib. +fun:*XImageDeleter* + # The follow entries are speculatively disabled. They're included in the # chromium build and include calls to dynamically resolved symbols; however, # they do not trigger cfi-icall failures in unit tests or normal chrome usage.
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py index cff977e..3eb563f 100755 --- a/tools/clang/scripts/build.py +++ b/tools/clang/scripts/build.py
@@ -577,7 +577,7 @@ compiler_rt_args = [ '-DCOMPILER_RT_BUILD_CRT=OFF', - '-DCOMPILER_RT_BUILD_LIBFUZZER=ON', + '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF', '-DCOMPILER_RT_BUILD_PROFILE=ON', '-DCOMPILER_RT_BUILD_SANITIZERS=ON', '-DCOMPILER_RT_BUILD_XRAY=OFF',
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py index 2083277..c4a3f74 100755 --- a/tools/clang/scripts/package.py +++ b/tools/clang/scripts/package.py
@@ -241,9 +241,6 @@ 'lib/clang/$V/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib', 'lib/clang/$V/lib/darwin/libclang_rt.asan_osx_dynamic.dylib', - # Fuzzing instrumentation (-fsanitize=fuzzer-no-link). - 'lib/clang/$V/lib/darwin/libclang_rt.fuzzer_no_main_osx.a', - # OS X and iOS builtin libraries (iossim is lipo'd into ios) for the # _IsOSVersionAtLeast runtime function. 'lib/clang/$V/lib/darwin/libclang_rt.ios.a', @@ -278,9 +275,6 @@ 'lib/clang/$V/lib/linux/libclang_rt.asan-arm-android.so', 'lib/clang/$V/lib/linux/libclang_rt.asan-i686-android.so', - # Fuzzing instrumentation (-fsanitize=fuzzer-no-link). - 'lib/clang/$V/lib/linux/libclang_rt.fuzzer_no_main-x86_64.a', - # HWASAN Android runtime. 'lib/clang/$V/lib/linux/libclang_rt.hwasan-aarch64-android.so', @@ -331,9 +325,6 @@ # AddressSanitizer C++ runtime. 'lib/clang/$V/lib/windows/clang_rt.asan_cxx-x86_64.lib', - # Fuzzing instrumentation (-fsanitize=fuzzer-no-link). - 'lib/clang/$V/lib/windows/clang_rt.fuzzer_no_main-x86_64.lib', - # Thunk for AddressSanitizer needed for static build of a shared lib. 'lib/clang/$V/lib/windows/clang_rt.asan_dll_thunk-x86_64.lib',
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index 00f81a7..d9f3611 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -39,7 +39,7 @@ # Reverting problematic clang rolls is safe, though. CLANG_REVISION = 'f7e52fbdb5a7af8ea0808e98458b497125a5eca1' CLANG_SVN_REVISION = '365097' -CLANG_SUB_REVISION = 2 +CLANG_SUB_REVISION = 5 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8], CLANG_SUB_REVISION)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 8017d2c..1a44933b 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -3996,6 +3996,36 @@ <description>Please enter the description of this user action.</description> </action> +<action name="ChromeColors_ColorApplied"> + <owner>gayane@chromium.org</owner> + <description>Chrome Colors was applied from Colors menu.</description> +</action> + +<action name="ChromeColors_DefaultApplied"> + <owner>gayane@chromium.org</owner> + <description>Default theme was applied from Colors menu.</description> +</action> + +<action name="ChromeColors_MenuCancel"> + <owner>gayane@chromium.org</owner> + <description>Applied changes where canceled from Colors menu.</description> +</action> + +<action name="ChromeColors_MenuDone"> + <owner>gayane@chromium.org</owner> + <description>Applied changes where confirmed from Colors menu.</description> +</action> + +<action name="ChromeColors_TabClosed"> + <owner>gayane@chromium.org</owner> + <description>Tab with open Colors menu was closed.</description> +</action> + +<action name="ChromeColors_ThemeUninstalled"> + <owner>gayane@chromium.org</owner> + <description>Webstore theme uninstalled from Colors menu.</description> +</action> + <action name="ChromeGeneratedCustomTab.StartedInitially"> <owner>tedchoc@chromium.org</owner> <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index a9ddef4..7d08cce4 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -24069,6 +24069,11 @@ <int value="10" label="FAILED_TO_CREATE_LEAF"/> <int value="11" label="UNHANDLED_TOKEN"/> <int value="12" label="TASK_QUEUE_STARVATION"/> + <int value="13" label="CONTENT_STORAGE_MISSING_ITEM"/> + <int value="14" label="ITEM_NOT_PARSED"/> + <int value="15" label="STORAGE_MISS_BEYOND_THRESHOLD"/> + <int value="16" label="CONTENT_MUTATION_FAILED"/> + <int value="17" label="ROOT_NOT_BOUND_TO_FEATURE"/> </enum> <enum name="FeedRequestReason"> @@ -40728,6 +40733,7 @@ <int value="12" label="Removed (max-age = 0)"/> <int value="13" label="Set (max-age > 0)"/> <int value="14" label="Discarded: missing remote endpoint"/> + <int value="15" label="Discarded: include_subdomains not allowed"/> </enum> <enum name="NetNetworkErrorLoggingRequestOutcome"> @@ -40783,6 +40789,7 @@ <int value="7" label="Parsed"/> <int value="8" label="Removed: TTL zero"/> <int value="9" label="Removed: empty"/> + <int value="10" label="Discarded: include_subdomains not allowed"/> </enum> <enum name="NetReportingHeaderEndpointOutcome"> @@ -60641,6 +60648,12 @@ <int value="3" label="kOtherFailure"/> </enum> +<enum name="WebApkDistributor"> + <int value="0" label="Browser"/> + <int value="1" label="Device Policy"/> + <int value="2" label="Other"/> +</enum> + <enum name="WebApkGooglePlayInstallResult"> <int value="0" label="Success"/> <int value="1" label="Install delegate unavailable"/> @@ -60725,6 +60738,12 @@ <int value="2" label="Not enough space to install."/> </enum> +<enum name="WebApkLifetimeLaunchCount"> + <int value="0" label="Never launched."/> + <int value="1" label="Launched once."/> + <int value="2" label="Launched multiple times."/> +</enum> + <enum name="WebApkOpenResult"> <int value="0" label="Open an installed WebAPK successfully"/> <int value="1" label="(Obsolete) Launch intent for WebAPK is null"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 3c7d6e0..25987fe 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -633,6 +633,16 @@ </summary> </histogram> +<histogram name="AccountManager.InitializationTime" units="microseconds" + expires_after="2020-01-31"> + <owner>sinhak@chromium.org</owner> + <owner>anastasiian@chromium.org</owner> + <summary> + Measures the time taken to fully initialize Chrome OS Account Manager. This + is recorded once per user session, at login time. + </summary> +</histogram> + <histogram name="AccountManager.Migrations.Result" enum="BooleanSuccess" expires_after="2019-11-05"> <owner>sinhak@chromium.org</owner> @@ -11983,13 +11993,12 @@ </histogram> <histogram name="BackgroundSync.Event.PeriodicResultPattern" - enum="BackgroundSyncResultPattern" expires_after="2016-01-25"> - <obsolete> - Stopped recording as of 1/2016. - </obsolete> - <owner>jkarlin@chromium.org</owner> + enum="BackgroundSyncResultPattern" expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> + <owner>peter@chromium.org</owner> <summary> - Records whether a one-shot sync event succeeded or failed and whether the + Records whether a periodic sync event succeeded or failed and whether the sync event finished in the foreground or background. </summary> <details> @@ -11999,11 +12008,10 @@ </histogram> <histogram name="BackgroundSync.Event.PeriodicStartedInForeground" - enum="BooleanInForeground" expires_after="2016-01-25"> - <obsolete> - Stopped recording as of 1/2016. - </obsolete> - <owner>jkarlin@chromium.org</owner> + enum="BooleanInForeground" expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> + <owner>peter@chromium.org</owner> <summary> Records whether a periodic sync event started firing in the foreground or background. Called shortly before the event is fired. @@ -12014,11 +12022,11 @@ <owner>nator@chromium.org</owner> <owner>rayankans@chromium.org</owner> <summary> - Time taken to execute a batch of sync events. A batch is defined as the set - of sync events dispatched at the same time by the BackgroundSyncManager. - Periodic syncs often run in a batch. One-shots usually run individually (a - batch of one), unless the device was offline and multiple are waiting for - the device to go back online. + Time taken to execute a batch of one-shot sync events. A batch is defined as + the set of sync events dispatched at the same time by the + BackgroundSyncManager. One-shots usually run individually (a batch of one), + unless the device was offline and multiple are waiting for the device to go + back online. </summary> </histogram> @@ -12136,7 +12144,6 @@ units="attempts" expires_after="2020-12-31"> <owner>nator@chromium.org</owner> <owner>rayankans@chromium.org</owner> - <owner>peter@chromium.org</owner> <summary> Records the number of times a sync event was dispatched for a one-shot Background Sync registration before it succeeded. @@ -12144,25 +12151,33 @@ </histogram> <histogram name="BackgroundSync.Registration.Periodic" - enum="BackgroundSyncStatus" expires_after="2016-01-25"> - <obsolete> - Stopped recording as of 1/2016. - </obsolete> - <owner>iclelland@chromium.org</owner> + enum="BackgroundSyncStatus" expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> <summary> - Records the result of attempting to register a periodic sync. + Records the result of attempting to register a periodic sync. This is called + at each successful registration. </summary> </histogram> <histogram name="BackgroundSync.Registration.Periodic.IsDuplicate" - enum="BooleanRegistrationIsDuplicate" expires_after="2016-01-25"> - <obsolete> - Stopped recording as of 1/2016. - </obsolete> - <owner>iclelland@chromium.org</owner> + enum="BooleanRegistrationIsDuplicate" expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> <summary> - Records whether a periodic sync registration exactly duplicates an existing - registered sync. + Records whether a periodic sync registration duplicates the tag of an + existing registered sync. Recorded upon each successful periodic Background + Sync registration. + </summary> +</histogram> + +<histogram name="BackgroundSync.Registration.Periodic.MinInterval" + units="seconds" expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> + <summary> + Records the minInterval requested, in seconds, when a periodic sync is + successfully registered. </summary> </histogram> @@ -40335,6 +40350,9 @@ <histogram name="Extensions.ExtensionManagement_RefreshTime" units="ms" expires_after="M77"> + <obsolete> + Deprecated 7/2019. + </obsolete> <owner>rkaplow@chromium.org</owner> <summary> The amount of time that elapsed during ExtensionManagement::Refresh. @@ -40485,8 +40503,9 @@ </histogram> <histogram name="Extensions.FeatureProviderStaticInitTime" units="ms" - expires_after="M77"> - <owner>rkaplow@chromium.org</owner> + expires_after="M90"> + <owner>rdevlin.cronin@chromium.org</owner> + <owner>extensions-core@chromium.org</owner> <summary> The amount of time that elapsed during extensions::FeatureProvider::Static. Only measured while in the browser process. @@ -40532,6 +40551,9 @@ <histogram name="Extensions.FontSettingsEventRouterCtorTime" units="ms" expires_after="M77"> + <obsolete> + Deprecated 7/2019. + </obsolete> <owner>rkaplow@chromium.org</owner> <summary> The amount of time that elapsed during the FontSettingsEventRouter @@ -41032,6 +41054,9 @@ <histogram name="Extensions.InitExtensionControlledPrefsTime" units="ms" expires_after="M77"> + <obsolete> + Deprecated 7/2019. + </obsolete> <owner>rkaplow@chromium.org</owner> <summary> The amount of time that elapsed during @@ -41040,8 +41065,9 @@ </histogram> <histogram name="Extensions.InitPrefGetExtensionsTime" units="ms" - expires_after="M77"> - <owner>rkaplow@chromium.org</owner> + expires_after="M90"> + <owner>rdevlin.cronin@chromium.org</owner> + <owner>extensions-core@chromium.org</owner> <summary> The amount of time that elapsed during the GetExtensions call within ExtensionPrefs::InitPrefStore. @@ -41049,6 +41075,9 @@ </histogram> <histogram name="Extensions.InitPrefStoreTime" units="ms"> + <obsolete> + Deprecated 7/2019. + </obsolete> <owner>rkaplow@chromium.org</owner> <summary> The amount of time that elapsed during ExtensionPrefs::InitPrefStore. @@ -42558,6 +42587,9 @@ <histogram name="Extensions.StorageFrontendInitTime" units="ms" expires_after="M77"> + <obsolete> + Deprecated 7/2019. + </obsolete> <owner>rkaplow@chromium.org</owner> <summary> The amount of time that elapsed during StorageFrontend::Init. @@ -84675,6 +84707,19 @@ </summary> </histogram> +<histogram base="true" + name="NQE.CongestionAnalyzer.CountInflightRequestsForPeakQueueingDelay" + units="requests" expires_after="M82"> + <owner>jfwang@google.com</owner> + <owner>tbansal@chromium.org</owner> + <summary> + Records the count of in-flight requests causing the peak queueing delay + within the current ongoing measurement period. These samples are bucketized + into 10 peak queueing delay levels. This is emitted at the end of the + current measurement period. + </summary> +</histogram> + <histogram name="NQE.ContentObserver.NetworkQualityMeaningfullyChanged" enum="BooleanChanged"> <owner>tbansal@chromium.org</owner> @@ -96983,6 +97028,28 @@ </summary> </histogram> +<histogram name="PeriodicBackgroundSync.Event.BatchSize" units="events" + expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> + <summary> + Records the number of sync events which were fired in a batch. A batch is + defined as the set of sync events dispatched at the same time by the + BackgroundSyncManager. Periodic syncs usually run in a batch. + </summary> +</histogram> + +<histogram name="PeriodicBackgroundSync.Event.Time" units="ms" + expires_after="2020-07-31"> + <owner>nator@chromium.org</owner> + <owner>rayankans@chromium.org</owner> + <summary> + Time taken to execute a batch of periodic sync events. A batch is defined as + the set of sync events dispatched at the same time by the + BackgroundSyncManager. Periodic syncs often run in a batch. + </summary> +</histogram> + <histogram name="Permissions.Action" enum="PermissionAction"> <owner>dominickn@chromium.org</owner> <owner>engedy@chromium.org</owner> @@ -121515,6 +121582,10 @@ name="ServiceWorker.StartWorker.InstalledScriptsSender.FinishedReason" enum="ServiceWorkerInstalledScriptsManager.FinishedReason" expires_after="M78"> + <obsolete> + Removed from code in July 2019 since ServiceWorkerScriptStreaming + successfully launched. + </obsolete> <owner>shimazu@chromium.org</owner> <summary> The result of sending installed scripts over @@ -161684,6 +161755,23 @@ name="NQE.WeightedAverage.Accuracy.HttpRTT.EstimatedObservedDiff.Positive.15"/> </histogram_suffixes> +<histogram_suffixes + name="NQE.CongestionAnalyzer.CountInflightRequestsForPeakQueueingDelay" + separator="."> + <suffix name="Level1" label="0-30"/> + <suffix name="Level2" label="30-60"/> + <suffix name="Level3" label="60-120"/> + <suffix name="Level4" label="120-250"/> + <suffix name="Level5" label="250-500"/> + <suffix name="Level6" label="500-1000"/> + <suffix name="Level7" label="1000-2000"/> + <suffix name="Level8" label="2000-4000"/> + <suffix name="Level9" label="4000-8000"/> + <suffix name="Level10" label=">=8000"/> + <affected-histogram + name="NQE.CongestionAnalyzer.CountInflightRequestsForPeakQueueingDelay"/> +</histogram_suffixes> + <histogram_suffixes name="NQE.DifferentPercentiles" separator="."> <obsolete> Deprecated 01/2018.
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index 41b51e1..56b251b4 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -6177,6 +6177,44 @@ </metric> </event> +<event name="PWA.Visit" singular="True"> + <owner>yfriedman@chromium.org</owner> + <owner>hartmanng@chromium.org</owner> + <owner>pkotwicz@chromium.org</owner> + <summary> + PWA usage metrics recorded during a user's interaction with the site. + </summary> + <metric name="LaunchSource" enum="LaunchFromHomeScreenSource"> + <summary> + Launch source of the WebAPK (tap from homescreen, WebShareTarget, + deeplink, etc). + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="WebAPKableSiteVisit" enum="Boolean"> + <summary> + Recorded every time a non-installed "WebAPKable" site is + visited. + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> +</event> + <event name="RendererSchedulerTask"> <owner>altimin@chromium.org</owner> <summary> @@ -7742,6 +7780,263 @@ </metric> </event> +<event name="WebAPK.Install" singular="True"> + <owner>yfriedman@chromium.org</owner> + <owner>hartmanng@chromium.org</owner> + <owner>pkotwicz@chromium.org</owner> + <summary> + WebAPK usage metrics recorded on Install. + </summary> + <metric name="AppVersion"> + <summary> + WebAPK app version that the user is installing. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> + <metric name="Distributor" enum="WebApkDistributor"> + <summary> + Distribution source of the WebAPK (browser, device policy, etc). + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="Install" enum="Boolean"> + <summary> + Recorded when this WebAPK is installed. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> +</event> + +<event name="WebAPK.SessionEnd" singular="True"> + <owner>yfriedman@chromium.org</owner> + <owner>hartmanng@chromium.org</owner> + <owner>pkotwicz@chromium.org</owner> + <summary> + WebAPK usage metrics recorded when the WebAPK is backgrounded. + </summary> + <metric name="AppVersion"> + <summary> + WebAPK app version that the user has installed. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> + <metric name="Distributor" enum="WebApkDistributor"> + <summary> + Distribution source of the WebAPK (browser, device policy, etc). + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="SessionDuration"> + <summary> + How long the user spent inside the WebAPK, in milliseconds. This will use + exponential bucketing (ie, count in 10s of milliseconds at first, then + 100s, then seconds, 10s of seconds, minutes, 10 minutes, hours, 10 hours, + days, weeks, years.) + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <quantiles type="std-percentiles"/> + </statistics> + </history> + </aggregation> + </metric> +</event> + +<event name="WebAPK.Uninstall" singular="True"> + <owner>yfriedman@chromium.org</owner> + <owner>hartmanng@chromium.org</owner> + <owner>pkotwicz@chromium.org</owner> + <summary> + WebAPK usage metrics recorded on Uninstall. + </summary> + <metric name="AppVersion"> + <summary> + WebAPK app version that the user has installed. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> + <metric name="Distributor" enum="WebApkDistributor"> + <summary> + Distribution source of the WebAPK (browser, device policy, etc). + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="InstalledDuration"> + <summary> + How long between Install and Uninstall of a particular WebAPK, in seconds. + This will use exponential bucketing (ie, count in seconds at first, then + 10s of seconds, minutes, 10 minutes, hours, 10 hours, days, weeks, years.) + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <quantiles type="std-percentiles"/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="LifetimeLaunches" enum="WebApkLifetimeLaunchCount"> + <summary> + Records how many times the WebAPK was launched between install and + uninstall (in coarse buckets). + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="Uninstall" enum="Boolean"> + <summary> + Recorded when this WebAPK is uninstalled. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> +</event> + +<event name="WebAPK.Visit" singular="True"> + <owner>yfriedman@chromium.org</owner> + <owner>hartmanng@chromium.org</owner> + <owner>pkotwicz@chromium.org</owner> + <summary> + WebAPK usage metrics recorded during a user's interaction with the site. + </summary> + <metric name="AppVersion"> + <summary> + WebAPK app version that the user has installed. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> + <metric name="Distributor" enum="WebApkDistributor"> + <summary> + Distribution source of the WebAPK (browser, device policy, etc). + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="Launch" enum="Boolean"> + <summary> + Recorded every time this WebAPK is launched. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + </history> + </aggregation> + </metric> + <metric name="LaunchSource" enum="LaunchFromHomeScreenSource"> + <summary> + Launch source of the WebAPK. + </summary> + <aggregation> + <history> + <index fields="metrics.Distributor"/> + <index fields="profile.country"/> + <index fields="profile.form_factor"/> + <index fields="profile.system_ram"/> + <statistics> + <enumeration/> + </statistics> + </history> + </aggregation> + </metric> +</event> + <event name="WebRTC.AddressHarvesting"> <owner>hta@chromium.org</owner> <summary>
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm index 83e5a43..926d12b 100644 --- a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm +++ b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
@@ -27,7 +27,7 @@ bool is_clipped = true; gfx::Rect clip_rect; - float clip_rect_corner_radius = 0; + gfx::RRectF rounded_corner_bounds; int sorting_context_id = 0; gfx::Transform transform; gfx::RectF contents_rect = gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f); @@ -66,7 +66,7 @@ CALayerProperties* properties) { return tree->ScheduleCALayer(ui::CARendererLayerParams( properties->is_clipped, properties->clip_rect, - properties->clip_rect_corner_radius, properties->sorting_context_id, + properties->rounded_corner_bounds, properties->sorting_context_id, properties->transform, properties->gl_image.get(), properties->contents_rect, properties->rect, properties->background_color, properties->edge_aa_mask, properties->opacity, properties->filter)); @@ -104,7 +104,7 @@ CALayerProperties properties; properties.allow_solid_color_layers = allow_solid_color_layers; properties.clip_rect = gfx::Rect(2, 4, 8, 16); - properties.clip_rect_corner_radius = 13; + properties.rounded_corner_bounds = gfx::RRectF(2, 4, 8, 16, 13); properties.transform.Translate(10, 20); properties.contents_rect = gfx::RectF(0.0f, 0.25f, 0.5f, 0.75f); properties.rect = gfx::Rect(16, 32, 64, 128); @@ -117,6 +117,7 @@ std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree; CALayer* root_layer = nil; CALayer* clip_and_sorting_layer = nil; + CALayer* clip_and_sorting_rounded_layer = nil; CALayer* transform_layer = nil; CALayer* content_layer = nil; @@ -133,7 +134,15 @@ EXPECT_EQ(1u, [[root_layer sublayers] count]); clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + + CALayer* superlayer_for_transform = clip_and_sorting_layer; + if (!properties.rounded_corner_bounds.IsEmpty()) { + clip_and_sorting_rounded_layer = + [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + superlayer_for_transform = clip_and_sorting_rounded_layer; + } + transform_layer = [[superlayer_for_transform sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[transform_layer sublayers] count]); content_layer = [[transform_layer sublayers] objectAtIndex:0]; @@ -141,8 +150,8 @@ EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); EXPECT_EQ(gfx::Rect(properties.clip_rect.size()), gfx::Rect([clip_and_sorting_layer bounds])); - EXPECT_EQ(properties.clip_rect_corner_radius, - [clip_and_sorting_layer cornerRadius]); + EXPECT_EQ(properties.rounded_corner_bounds.GetSimpleRadius(), + [clip_and_sorting_rounded_layer cornerRadius]); EXPECT_EQ(properties.clip_rect.origin(), gfx::Point([clip_and_sorting_layer position])); EXPECT_EQ(-properties.clip_rect.origin().x(), @@ -184,8 +193,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); // Validate the clip and sorting context layer. EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); @@ -211,8 +223,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); // Validate the clip and sorting context layer. EXPECT_FALSE([clip_and_sorting_layer masksToBounds]); @@ -235,8 +250,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); // Validate the transform layer. EXPECT_EQ(properties.transform.matrix().get(3, 0), @@ -257,9 +275,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. Note that top and bottom edges flip. @@ -278,9 +298,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. Note that edge anti-aliasing does not flip @@ -308,9 +330,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. @@ -331,9 +355,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. @@ -354,9 +380,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. @@ -375,9 +403,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. @@ -399,9 +429,11 @@ EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, + EXPECT_EQ(clip_and_sorting_rounded_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); // Validate the content layer. @@ -424,9 +456,16 @@ [[root_layer sublayers] objectAtIndex:0]); clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + + EXPECT_NE(clip_and_sorting_rounded_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + clip_and_sorting_rounded_layer = + [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); EXPECT_NE(transform_layer, [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + transform_layer = + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[transform_layer sublayers] count]); EXPECT_NE(content_layer, [[transform_layer sublayers] objectAtIndex:0]); content_layer = [[transform_layer sublayers] objectAtIndex:0]; @@ -468,6 +507,73 @@ if ([content_layer respondsToSelector:(@selector(contentsScale))]) EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]); } + + // Remove the rounded corners. This should result in the rounded corners + // being removed on that layer. + { + properties.rounded_corner_bounds = gfx::RRectF(); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_rounded_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(0, [clip_and_sorting_rounded_layer cornerRadius]); + EXPECT_FALSE([clip_and_sorting_rounded_layer masksToBounds]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + } + + { + // A no-op update should not invalidate any of the layers. + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_rounded_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_rounded_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + } + + // Re-add rounded corners. + { + properties.rounded_corner_bounds = gfx::RRectF(1, 2, 3, 4, 5); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_rounded_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(properties.rounded_corner_bounds.GetSimpleRadius(), + [clip_and_sorting_rounded_layer cornerRadius]); + EXPECT_TRUE([clip_and_sorting_rounded_layer masksToBounds]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_rounded_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + } } };
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h index 3c3fc18..08057cb 100644 --- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h +++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -19,6 +19,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/mac/io_surface.h" +#include "ui/gfx/rrect_f.h" #include "ui/gfx/transform.h" @class AVSampleBufferDisplayLayer; @@ -107,7 +108,7 @@ struct ClipAndSortingLayer { ClipAndSortingLayer(bool is_clipped, gfx::Rect clip_rect, - float clip_rect_corner_radius, + gfx::RRectF rounded_corner_bounds, unsigned sorting_context_id, bool is_singleton_sorting_context); ClipAndSortingLayer(ClipAndSortingLayer&& layer); @@ -124,10 +125,11 @@ std::vector<TransformLayer> transform_layers; bool is_clipped = false; gfx::Rect clip_rect; - float clip_rect_corner_radius = 0; + gfx::RRectF rounded_corner_bounds; unsigned sorting_context_id = 0; bool is_singleton_sorting_context = false; - base::scoped_nsobject<CALayer> ca_layer; + base::scoped_nsobject<CALayer> clipping_ca_layer; + base::scoped_nsobject<CALayer> rounded_corner_ca_layer; private: DISALLOW_COPY_AND_ASSIGN(ClipAndSortingLayer);
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm index 1903b0f..787741d5 100644 --- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm +++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -331,12 +331,12 @@ CARendererLayerTree::ClipAndSortingLayer::ClipAndSortingLayer( bool is_clipped, gfx::Rect clip_rect, - float clip_rect_corner_radius, + gfx::RRectF rounded_corner_bounds_arg, unsigned sorting_context_id, bool is_singleton_sorting_context) : is_clipped(is_clipped), clip_rect(clip_rect), - clip_rect_corner_radius(clip_rect_corner_radius), + rounded_corner_bounds(rounded_corner_bounds_arg), sorting_context_id(sorting_context_id), is_singleton_sorting_context(is_singleton_sorting_context) {} @@ -345,19 +345,22 @@ : transform_layers(std::move(layer.transform_layers)), is_clipped(layer.is_clipped), clip_rect(layer.clip_rect), - clip_rect_corner_radius(layer.clip_rect_corner_radius), + rounded_corner_bounds(layer.rounded_corner_bounds), sorting_context_id(layer.sorting_context_id), is_singleton_sorting_context(layer.is_singleton_sorting_context), - ca_layer(layer.ca_layer) { + clipping_ca_layer(layer.clipping_ca_layer), + rounded_corner_ca_layer(layer.rounded_corner_ca_layer) { // Ensure that the ca_layer be reset, so that when the destructor is called, // the layer hierarchy is unaffected. // TODO(ccameron): Add a move constructor for scoped_nsobject to do this // automatically. - layer.ca_layer.reset(); + layer.clipping_ca_layer.reset(); + layer.rounded_corner_ca_layer.reset(); } CARendererLayerTree::ClipAndSortingLayer::~ClipAndSortingLayer() { - [ca_layer removeFromSuperlayer]; + [clipping_ca_layer removeFromSuperlayer]; + [rounded_corner_ca_layer removeFromSuperlayer]; } CARendererLayerTree::TransformLayer::TransformLayer( @@ -510,8 +513,7 @@ current_layer.sorting_context_id == params.sorting_context_id && (current_layer.is_clipped != params.is_clipped || current_layer.clip_rect != params.clip_rect || - current_layer.clip_rect_corner_radius != - params.clip_rect_corner_radius)) { + current_layer.rounded_corner_bounds != params.rounded_corner_bounds)) { DLOG(ERROR) << "CALayer changed clip inside non-zero sorting context."; return false; } @@ -519,15 +521,14 @@ !current_layer.is_singleton_sorting_context && current_layer.is_clipped == params.is_clipped && current_layer.clip_rect == params.clip_rect && - current_layer.clip_rect_corner_radius == - params.clip_rect_corner_radius && + current_layer.rounded_corner_bounds == params.rounded_corner_bounds && current_layer.sorting_context_id == params.sorting_context_id) { needs_new_clip_and_sorting_layer = false; } } if (needs_new_clip_and_sorting_layer) { clip_and_sorting_layers.push_back(ClipAndSortingLayer( - params.is_clipped, params.clip_rect, params.clip_rect_corner_radius, + params.is_clipped, params.clip_rect, params.rounded_corner_bounds, params.sorting_context_id, is_singleton_sorting_context)); } clip_and_sorting_layers.back().AddContentLayer(tree, params); @@ -624,52 +625,82 @@ float scale_factor) { bool update_is_clipped = true; bool update_clip_rect = true; - bool update_corner_radius = true; if (old_layer) { - DCHECK(old_layer->ca_layer); - std::swap(ca_layer, old_layer->ca_layer); + DCHECK(old_layer->clipping_ca_layer); + DCHECK(old_layer->rounded_corner_ca_layer); + std::swap(clipping_ca_layer, old_layer->clipping_ca_layer); + std::swap(rounded_corner_ca_layer, old_layer->rounded_corner_ca_layer); update_is_clipped = old_layer->is_clipped != is_clipped; update_clip_rect = update_is_clipped || old_layer->clip_rect != clip_rect; - update_corner_radius = - update_is_clipped || - old_layer->clip_rect_corner_radius != clip_rect_corner_radius; } else { - ca_layer.reset([[CALayer alloc] init]); - [ca_layer setAnchorPoint:CGPointZero]; - [superlayer addSublayer:ca_layer]; + clipping_ca_layer.reset([[CALayer alloc] init]); + [clipping_ca_layer setAnchorPoint:CGPointZero]; + [superlayer addSublayer:clipping_ca_layer]; + rounded_corner_ca_layer.reset([[CALayer alloc] init]); + [rounded_corner_ca_layer setAnchorPoint:CGPointZero]; + [clipping_ca_layer addSublayer:rounded_corner_ca_layer]; } - if ([ca_layer superlayer] != superlayer) { + + if (!rounded_corner_bounds.IsEmpty()) { + if (!old_layer || + old_layer->rounded_corner_bounds != rounded_corner_bounds) { + gfx::RectF dip_rounded_corner_bounds = + gfx::RectF(rounded_corner_bounds.rect()); + dip_rounded_corner_bounds.Scale(1 / scale_factor); + + [rounded_corner_ca_layer setMasksToBounds:true]; + + [rounded_corner_ca_layer + setPosition:CGPointMake(dip_rounded_corner_bounds.x(), + dip_rounded_corner_bounds.y())]; + [rounded_corner_ca_layer + setBounds:CGRectMake(0, 0, dip_rounded_corner_bounds.width(), + dip_rounded_corner_bounds.height())]; + [rounded_corner_ca_layer + setSublayerTransform:CATransform3DMakeTranslation( + -dip_rounded_corner_bounds.x(), + -dip_rounded_corner_bounds.y(), 0)]; + + [rounded_corner_ca_layer + setCornerRadius:rounded_corner_bounds.GetSimpleRadius()]; + } + } else { + [rounded_corner_ca_layer setMasksToBounds:false]; + [rounded_corner_ca_layer setPosition:CGPointZero]; + [rounded_corner_ca_layer setBounds:CGRectZero]; + [rounded_corner_ca_layer setSublayerTransform:CATransform3DIdentity]; + [rounded_corner_ca_layer setCornerRadius:0]; + } + if ([clipping_ca_layer superlayer] != superlayer) { DLOG(ERROR) << "CARendererLayerTree root layer not attached to tree."; } if (update_is_clipped) - [ca_layer setMasksToBounds:is_clipped]; + [clipping_ca_layer setMasksToBounds:is_clipped]; if (update_clip_rect) { if (is_clipped) { gfx::RectF dip_clip_rect = gfx::RectF(clip_rect); dip_clip_rect.Scale(1 / scale_factor); - [ca_layer setPosition:CGPointMake(dip_clip_rect.x(), dip_clip_rect.y())]; - [ca_layer setBounds:CGRectMake(0, 0, dip_clip_rect.width(), - dip_clip_rect.height())]; - [ca_layer + [clipping_ca_layer + setPosition:CGPointMake(dip_clip_rect.x(), dip_clip_rect.y())]; + [clipping_ca_layer setBounds:CGRectMake(0, 0, dip_clip_rect.width(), + dip_clip_rect.height())]; + [clipping_ca_layer setSublayerTransform:CATransform3DMakeTranslation( -dip_clip_rect.x(), -dip_clip_rect.y(), 0)]; } else { - [ca_layer setPosition:CGPointZero]; - [ca_layer setBounds:CGRectZero]; - [ca_layer setSublayerTransform:CATransform3DIdentity]; + [clipping_ca_layer setPosition:CGPointZero]; + [clipping_ca_layer setBounds:CGRectZero]; + [clipping_ca_layer setSublayerTransform:CATransform3DIdentity]; } } - if (update_corner_radius) - [ca_layer setCornerRadius:clip_rect_corner_radius]; - for (size_t i = 0; i < transform_layers.size(); ++i) { TransformLayer* old_transform_layer = nullptr; if (old_layer && i < old_layer->transform_layers.size()) old_transform_layer = &old_layer->transform_layers[i]; - transform_layers[i].CommitToCA(ca_layer.get(), old_transform_layer, + transform_layers[i].CommitToCA(rounded_corner_ca_layer, old_transform_layer, scale_factor); } }
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn index 3739e93..99e228be 100644 --- a/ui/accessibility/BUILD.gn +++ b/ui/accessibility/BUILD.gn
@@ -39,6 +39,8 @@ "ax_action_data.h", "ax_action_handler.cc", "ax_action_handler.h", + "ax_active_popup.cc", + "ax_active_popup.h", "ax_clipping_behavior.h", "ax_coordinate_system.h", "ax_enum_util.cc",
diff --git a/ui/accessibility/ax_active_popup.cc b/ui/accessibility/ax_active_popup.cc new file mode 100644 index 0000000..570838f --- /dev/null +++ b/ui/accessibility/ax_active_popup.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 "ui/accessibility/ax_active_popup.h" + +namespace ui { +// Represents a global storage for the view accessibility for an +// autofill popup. It is a singleton wrapper around the ax unique id of the +// autofill popup. This singleton is used for communicating the live status of +// the autofill popup between web contents and views. +// The assumption here is that only one autofill popup can exist at a time. +static base::NoDestructor<base::Optional<int32_t>> g_active_popup_ax_unique_id; + +base::Optional<int32_t> GetActivePopupAxUniqueId() { + return *g_active_popup_ax_unique_id; +} + +void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id) { + // When an instance of autofill popup hides, the caller of popup hide should + // make sure g_active_popup_ax_unique_id is cleared. The assumption is that + // there can only be one active autofill popup existing at a time. If on + // popup showing, we encounter g_active_popup_ax_unique_id is already set, + // this would indicate two autofill popups are showing at the same time or + // previous on popup hide call did not clear the variable, so we should fail + // DCHECK here. + DCHECK(!GetActivePopupAxUniqueId()); + + *g_active_popup_ax_unique_id = ax_unique_id; +} + +void ClearActivePopupAxUniqueId() { + *g_active_popup_ax_unique_id = base::nullopt; +} + +} // namespace ui
diff --git a/ui/accessibility/ax_active_popup.h b/ui/accessibility/ax_active_popup.h new file mode 100644 index 0000000..111c8c54 --- /dev/null +++ b/ui/accessibility/ax_active_popup.h
@@ -0,0 +1,22 @@ +// 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 UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_ +#define UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_ + +#include "base/macros.h" +#include "base/no_destructor.h" +#include "base/optional.h" +#include "ui/accessibility/ax_export.h" + +namespace ui { +AX_EXPORT base::Optional<int32_t> GetActivePopupAxUniqueId(); + +AX_EXPORT void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id); + +AX_EXPORT void ClearActivePopupAxUniqueId(); + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index 6e06fc74..643e409 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -28,6 +28,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/accessibility_switches.h" #include "ui/accessibility/ax_action_data.h" +#include "ui/accessibility/ax_active_popup.h" #include "ui/accessibility/ax_mode_observer.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_node_position.h" @@ -389,54 +390,88 @@ } } +std::vector<AXPlatformNodeWin*> +AXPlatformNodeWin::CreatePlatformNodeVectorFromRelationIdVector( + std::vector<int32_t>& relation_id_list) { + std::vector<AXPlatformNodeWin*> platform_node_list; + + for (int32_t id : relation_id_list) { + AXPlatformNode* platform_node = GetDelegate()->GetFromNodeID(id); + if (IsValidUiaRelationTarget(platform_node)) { + platform_node_list.push_back( + static_cast<AXPlatformNodeWin*>(platform_node)); + } + } + + return platform_node_list; +} + +SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsSafeArray( + std::vector<AXPlatformNodeWin*>& platform_node_list) { + if (platform_node_list.empty()) + return nullptr; + + SAFEARRAY* uia_array = + SafeArrayCreateVector(VT_UNKNOWN, 0, platform_node_list.size()); + LONG i = 0; + + for (AXPlatformNodeWin* platform_node : platform_node_list) { + // All incoming ids should already be validated to have a valid relation + // targets so that this function does not need to re-check before allocating + // the SAFEARRAY. + DCHECK(IsValidUiaRelationTarget(platform_node)); + SafeArrayPutElement(uia_array, &i, + static_cast<IRawElementProviderSimple*>(platform_node)); + ++i; + } + + return uia_array; +} + +SAFEARRAY* AXPlatformNodeWin::CreateUIAControllerForArray() { + std::vector<int32_t> relation_id_list = + GetIntListAttribute(ax::mojom::IntListAttribute::kControlsIds); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(relation_id_list); + + if (GetActivePopupAxUniqueId() != base::nullopt) { + AXPlatformNodeWin* view_popup_node_win = static_cast<AXPlatformNodeWin*>( + GetFromUniqueId(GetActivePopupAxUniqueId().value())); + + if (IsValidUiaRelationTarget(view_popup_node_win)) + platform_node_list.push_back(view_popup_node_win); + } + + return CreateUIAElementsSafeArray(platform_node_list); +} + SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsArrayForRelation( const ax::mojom::IntListAttribute& attribute) { - std::vector<int32_t> id_list = GetIntListAttribute(attribute); - base::EraseIf(id_list, [&](int32_t node_id) { - return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id)); - }); - SAFEARRAY* propertyvalue = CreateUIAElementsArrayFromIdVector(id_list); - return propertyvalue; + std::vector<int32_t> relation_id_list = GetIntListAttribute(attribute); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(relation_id_list); + + return CreateUIAElementsSafeArray(platform_node_list); } SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsArrayForReverseRelation( const ax::mojom::IntListAttribute& attribute) { std::set<AXPlatformNode*> reverse_relations = GetDelegate()->GetReverseRelations(attribute); - std::vector<int32_t> ax_platform_node_ids; - ax_platform_node_ids.reserve(reverse_relations.size()); - for (AXPlatformNode* ax_platform_node : reverse_relations) { - if (IsValidUiaRelationTarget(ax_platform_node)) { - ax_platform_node_ids.push_back( - static_cast<AXPlatformNodeWin*>(ax_platform_node)->GetData().id); - } - } - return CreateUIAElementsArrayFromIdVector(ax_platform_node_ids); -} + std::vector<int32_t> id_list; + std::transform( + reverse_relations.cbegin(), reverse_relations.cend(), + std::back_inserter(id_list), [](AXPlatformNode* platform_node) { + return static_cast<AXPlatformNodeWin*>(platform_node)->GetData().id; + }); -SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsArrayFromIdVector( - std::vector<int32_t>& ids) { - if (ids.size() == 0) - return nullptr; + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(id_list); - SAFEARRAY* uia_array = SafeArrayCreateVector(VT_UNKNOWN, 0, ids.size()); - - LONG i = 0; - for (const auto& node_id : ids) { - AXPlatformNodeWin* node_win = - static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id)); - - // The caller should validate that all incoming ids are valid relation - // targets so that this function does not need to re-check before allocating - // the SAFEARRAY. - DCHECK(IsValidUiaRelationTarget(node_win)); - SafeArrayPutElement(uia_array, &i, - static_cast<IRawElementProviderSimple*>(node_win)); - ++i; - } - - return uia_array; + return CreateUIAElementsSafeArray(platform_node_list); } SAFEARRAY* AXPlatformNodeWin::CreateClickablePointArray() { @@ -2138,10 +2173,11 @@ std::vector<int32_t> column_header_ids = GetDelegate()->GetColHeaderNodeIds(*column); - base::EraseIf(column_header_ids, [this](int32_t node_id) { - return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id)); - }); - *result = CreateUIAElementsArrayFromIdVector(column_header_ids); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(column_header_ids); + + *result = CreateUIAElementsSafeArray(platform_node_list); return S_OK; } @@ -2155,10 +2191,11 @@ std::vector<int32_t> row_header_ids = GetDelegate()->GetRowHeaderNodeIds(*row); - base::EraseIf(row_header_ids, [this](int32_t node_id) { - return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id)); - }); - *result = CreateUIAElementsArrayFromIdVector(row_header_ids); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(row_header_ids); + + *result = CreateUIAElementsSafeArray(platform_node_list); return S_OK; } @@ -2171,10 +2208,11 @@ UIA_VALIDATE_CALL_1_ARG(result); std::vector<int32_t> column_header_ids = GetDelegate()->GetColHeaderNodeIds(); - base::EraseIf(column_header_ids, [this](int32_t node_id) { - return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id)); - }); - *result = CreateUIAElementsArrayFromIdVector(column_header_ids); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(column_header_ids); + + *result = CreateUIAElementsSafeArray(platform_node_list); return S_OK; } @@ -2183,10 +2221,11 @@ UIA_VALIDATE_CALL_1_ARG(result); std::vector<int32_t> row_header_ids = GetDelegate()->GetRowHeaderNodeIds(); - base::EraseIf(row_header_ids, [this](int32_t node_id) { - return !IsValidUiaRelationTarget(GetDelegate()->GetFromNodeID(node_id)); - }); - *result = CreateUIAElementsArrayFromIdVector(row_header_ids); + + std::vector<AXPlatformNodeWin*> platform_node_list = + CreatePlatformNodeVectorFromRelationIdVector(row_header_ids); + + *result = CreateUIAElementsSafeArray(platform_node_list); return S_OK; } @@ -3735,8 +3774,7 @@ case UIA_ControllerForPropertyId: result->vt = VT_ARRAY | VT_UNKNOWN; - result->parray = CreateUIAElementsArrayForRelation( - ax::mojom::IntListAttribute::kControlsIds); + result->parray = CreateUIAControllerForArray(); break; case UIA_ControlTypePropertyId:
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h index 76a5545..2dfbbbc 100644 --- a/ui/accessibility/platform/ax_platform_node_win.h +++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -1165,15 +1165,30 @@ SAFEARRAY* CreateUIAElementsArrayForRelation( const ax::mojom::IntListAttribute& attribute); + // Return an array of automation elements based on the attribute + // IntList::kControlsIds for web content and IntAttribute::kViewPopupId. These + // two attributes denote the controllees, web content elements and view popup + // element respectively. + // The function will skip over any ids that cannot be resolved. + SAFEARRAY* CreateUIAControllerForArray(); + // Return an unordered array of automation elements which reference this node // for the given attribute. SAFEARRAY* CreateUIAElementsArrayForReverseRelation( const ax::mojom::IntListAttribute& attribute); - // Return an array of automation elements given a vector of |AXNode| ids. - // The caller should validate that all of the given ids are valid relation - // targets. - SAFEARRAY* CreateUIAElementsArrayFromIdVector(std::vector<int32_t>& ids); + // Return a vector of AXPlatformNodeWin referenced by the ids in function + // argument. The function will skip over any ids that cannot be resolved as + // valid relation target. + std::vector<AXPlatformNodeWin*> CreatePlatformNodeVectorFromRelationIdVector( + std::vector<int32_t>& relation_id_list); + + // Create a safearray of automation elements from a vector of + // AXPlatformNodeWin. + // The caller should validate that all of the given ax platform nodes are + // valid relation targets. + SAFEARRAY* CreateUIAElementsSafeArray( + std::vector<AXPlatformNodeWin*>& platform_node_list); // Return an array that contains the center x, y coordinates of the // clickable point.
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc index 6cea60f..a052790 100644 --- a/ui/base/x/x11_util.cc +++ b/ui/base/x/x11_util.cc
@@ -194,15 +194,6 @@ return min_dimension > 0 ? min_dimension : 64; } -// Custom release function that will be passed to Skia so that it deletes the -// image when the SkBitmap goes out of scope. -// |address| is the pointer to the data inside the XImage. -// |context| is the pointer to the XImage. -void ReleaseXImage(void* address, void* context) { - if (context) - XDestroyImage(static_cast<XImage*>(context)); -} - // A process wide singleton cache for custom X cursors. class XCustomCursorCache { public: @@ -1108,51 +1099,6 @@ return result; } -bool CopyAreaToCanvas(XID drawable, - gfx::Rect source_bounds, - gfx::Point dest_offset, - gfx::Canvas* canvas) { - std::unique_ptr<XImage, XImageDeleter> image(XGetImage( - gfx::GetXDisplay(), drawable, source_bounds.x(), source_bounds.y(), - source_bounds.width(), source_bounds.height(), AllPlanes, ZPixmap)); - if (!image) { - LOG(ERROR) << "XGetImage failed"; - return false; - } - - if (image->bits_per_pixel == 32) { - if ((0xff << SK_R32_SHIFT) != image->red_mask || - (0xff << SK_G32_SHIFT) != image->green_mask || - (0xff << SK_B32_SHIFT) != image->blue_mask) { - LOG(WARNING) << "XImage and Skia byte orders differ"; - return false; - } - - // Set the alpha channel before copying to the canvas. Otherwise, areas of - // the framebuffer that were cleared by ply-image rather than being obscured - // by an image during boot may end up transparent. - // TODO(derat|marcheu): Remove this if/when ply-image has been updated to - // set the framebuffer's alpha channel regardless of whether the device - // claims to support alpha or not. - for (int i = 0; i < image->width * image->height * 4; i += 4) - image->data[i + 3] = 0xff; - - SkBitmap bitmap; - bitmap.installPixels( - SkImageInfo::MakeN32Premul(image->width, image->height), image->data, - image->bytes_per_line, &ReleaseXImage, image.release()); - gfx::ImageSkia image_skia; - gfx::ImageSkiaRep image_rep(bitmap, canvas->image_scale()); - image_skia.AddRepresentation(image_rep); - canvas->DrawImageInt(image_skia, dest_offset.x(), dest_offset.y()); - } else { - NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel; - return false; - } - - return true; -} - WindowManagerName GuessWindowManager() { std::string name; if (!GetWindowManagerName(&name)) @@ -1338,11 +1284,6 @@ cursor_ = cursor; } -void XImageDeleter::operator()(XImage* image) const { - if (image) - XDestroyImage(image); -} - namespace test { const XcursorImage* GetCachedXcursorImage(::Cursor cursor) {
diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h index 050ea9cd..08ba2be 100644 --- a/ui/base/x/x11_util.h +++ b/ui/base/x/x11_util.h
@@ -30,7 +30,6 @@ typedef unsigned long Cursor; namespace gfx { -class Canvas; class Insets; class Point; class Rect; @@ -250,17 +249,6 @@ COMPONENT_EXPORT(UI_BASE_X) bool GetXWindowStack(XID window, std::vector<XID>* windows); -// Copies |source_bounds| from |drawable| to |canvas| at offset |dest_offset|. -// |source_bounds| is in physical pixels, while |dest_offset| is relative to -// the canvas's scale. Note that this function is slow since it uses -// XGetImage() to copy the data from the X server to this process before -// copying it to |canvas|. -COMPONENT_EXPORT(UI_BASE_X) -bool CopyAreaToCanvas(XID drawable, - gfx::Rect source_bounds, - gfx::Point dest_offset, - gfx::Canvas* canvas); - enum WindowManagerName { WM_OTHER, // We were able to obtain the WM's name, but there is // no corresponding entry in this enum. @@ -357,10 +345,6 @@ DISALLOW_COPY_AND_ASSIGN(XScopedCursor); }; -struct COMPONENT_EXPORT(UI_BASE_X) XImageDeleter { - void operator()(XImage* image) const; -}; - namespace test { // Returns the cached XcursorImage for |cursor|.
diff --git a/ui/events/blink/blink_features.cc b/ui/events/blink/blink_features.cc index a2423c48..42b5616f 100644 --- a/ui/events/blink/blink_features.cc +++ b/ui/events/blink/blink_features.cc
@@ -14,10 +14,10 @@ const base::Feature kUpdateHoverFromLayoutChangeAtBeginFrame{ "UpdateHoverFromLayoutChangeAtBeginFrame", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kUpdateHoverFromScrollAtBeginFrame{ - "UpdateHoverFromScrollAtBeginFrame", base::FEATURE_DISABLED_BY_DEFAULT}; + "UpdateHoverFromScrollAtBeginFrame", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kCompositorTouchAction{"CompositorTouchAction", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc index 34950f2..c062dfa0 100644 --- a/ui/events/blink/input_handler_proxy_unittest.cc +++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -505,7 +505,7 @@ ui::InputPredictor::InputData* result) { return input_handler_proxy_.scroll_predictor_->predictor_ ->GeneratePrediction(WebInputEvent::GetStaticTimeStampForTests(), - false /* is_resampling */, result); + result); } protected:
diff --git a/ui/events/blink/prediction/empty_predictor.cc b/ui/events/blink/prediction/empty_predictor.cc index 4d33485..cbb2237 100644 --- a/ui/events/blink/prediction/empty_predictor.cc +++ b/ui/events/blink/prediction/empty_predictor.cc
@@ -18,7 +18,7 @@ } void EmptyPredictor::Reset() { - last_input_.time_stamp = base::TimeTicks(); + last_input_ = base::nullopt; } void EmptyPredictor::Update(const InputData& cur_input) { @@ -26,17 +26,16 @@ } bool EmptyPredictor::HasPrediction() const { - return false; + return last_input_ != base::nullopt; } -bool EmptyPredictor::GeneratePrediction(base::TimeTicks frame_time, - bool is_resampling, +bool EmptyPredictor::GeneratePrediction(base::TimeTicks predict_time, InputData* result) const { - if (!last_input_.time_stamp.is_null()) { - result->pos = last_input_.pos; - return true; - } - return false; + if (!HasPrediction()) + return false; + + result->pos = last_input_.value().pos; + return true; } } // namespace ui
diff --git a/ui/events/blink/prediction/empty_predictor.h b/ui/events/blink/prediction/empty_predictor.h index 5a62ab4..35790b1 100644 --- a/ui/events/blink/prediction/empty_predictor.h +++ b/ui/events/blink/prediction/empty_predictor.h
@@ -5,6 +5,7 @@ #ifndef UI_EVENTS_BLINK_PREDICTION_EMPTY_PREDICTOR_H_ #define UI_EVENTS_BLINK_PREDICTION_EMPTY_PREDICTOR_H_ +#include "base/optional.h" #include "ui/events/blink/prediction/input_predictor.h" namespace ui { @@ -26,13 +27,12 @@ bool HasPrediction() const override; // Returns the last_input_ for testing. - bool GeneratePrediction(base::TimeTicks frame_time, - bool is_resampling, + bool GeneratePrediction(base::TimeTicks predict_time, InputData* result) const override; private: // store the last_input_ point for testing - InputData last_input_; + base::Optional<InputData> last_input_; DISALLOW_COPY_AND_ASSIGN(EmptyPredictor); };
diff --git a/ui/events/blink/prediction/input_predictor.h b/ui/events/blink/prediction/input_predictor.h index f146a1e..92c218e 100644 --- a/ui/events/blink/prediction/input_predictor.h +++ b/ui/events/blink/prediction/input_predictor.h
@@ -39,16 +39,17 @@ // Generate the prediction based on current points. virtual bool GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const = 0; + // Returns the maximum of prediction available for the predictor + // before having side effects (jitter, wrong orientation, etc..) + const base::TimeDelta MaxResampleTime() const { return kMaxResampleTime; } + protected: static constexpr base::TimeDelta kMaxTimeDelta = base::TimeDelta::FromMilliseconds(20); - // When event is jammed in OS or browser, we might have events with invalid - // timestamp. To avoid getting inaccurate result, limit the resampling time - // delta to 20 ms. This value might change if we have better timestamp or - // do better in predicting. + + // Maximum amount of prediction when resampling static constexpr base::TimeDelta kMaxResampleTime = base::TimeDelta::FromMilliseconds(20); };
diff --git a/ui/events/blink/prediction/input_predictor_unittest_helpers.cc b/ui/events/blink/prediction/input_predictor_unittest_helpers.cc index 6fc028b..11fede3 100644 --- a/ui/events/blink/prediction/input_predictor_unittest_helpers.cc +++ b/ui/events/blink/prediction/input_predictor_unittest_helpers.cc
@@ -17,9 +17,8 @@ for (size_t i = 0; i < timestamp_ms.size(); i++) { if (predictor_->HasPrediction()) { ui::InputPredictor::InputData result; - EXPECT_TRUE( - predictor_->GeneratePrediction(FromMilliseconds(timestamp_ms[i]), - false /* is_resampling */, &result)); + EXPECT_TRUE(predictor_->GeneratePrediction( + FromMilliseconds(timestamp_ms[i]), &result)); EXPECT_NEAR(result.pos.x(), x[i], kEpsilon); EXPECT_NEAR(result.pos.y(), y[i], kEpsilon); } @@ -48,8 +47,7 @@ if (predictor_->HasPrediction()) { InputPredictor::InputData result; EXPECT_TRUE(predictor_->GeneratePrediction( - FromMilliseconds(prediction_ts_ms[current_prediction_ts]), - false /* is_resampling */, &result)); + FromMilliseconds(prediction_ts_ms[current_prediction_ts]), &result)); computed_x.push_back(result.pos.x()); computed_y.push_back(result.pos.y()); current_prediction_ts++;
diff --git a/ui/events/blink/prediction/kalman_predictor.cc b/ui/events/blink/prediction/kalman_predictor.cc index 249c93d..8c03b44 100644 --- a/ui/events/blink/prediction/kalman_predictor.cc +++ b/ui/events/blink/prediction/kalman_predictor.cc
@@ -60,32 +60,21 @@ } bool KalmanPredictor::GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const { + if (!HasPrediction()) + return false; + + float pred_dt = (predict_time - last_point_.time_stamp).InMillisecondsF(); + std::vector<InputData> pred_points; - - base::TimeDelta dt = predict_time - last_point_.time_stamp; - // Kalman filter is not very good when predicting backwards. Besides, - // predicting backwards means increasing latency. Thus disable prediction when - // dt < 0. - if (!HasPrediction() || dt < base::TimeDelta()) - return false; - - // For resampling, we don't want to predict too far away because the result - // will likely be inaccurate in that case. But it may be useful for predicted - // points. - if (is_resampling && dt > kMaxResampleTime) - return false; - gfx::Vector2dF position(last_point_.pos.x(), last_point_.pos.y()); // gfx::Vector2dF position = PredictPosition(); gfx::Vector2dF velocity = PredictVelocity(); gfx::Vector2dF acceleration = PredictAcceleration(); - float dt_ms = dt.InMillisecondsF(); position += - ScaleVector2d(velocity, kVelocityInfluence * dt_ms) + - ScaleVector2d(acceleration, kAccelerationInfluence * dt_ms * dt_ms); + ScaleVector2d(velocity, kVelocityInfluence * pred_dt) + + ScaleVector2d(acceleration, kAccelerationInfluence * pred_dt * pred_dt); result->pos.set_x(position.x()); result->pos.set_y(position.y());
diff --git a/ui/events/blink/prediction/kalman_predictor.h b/ui/events/blink/prediction/kalman_predictor.h index 6b373f5..fc5a200 100644 --- a/ui/events/blink/prediction/kalman_predictor.h +++ b/ui/events/blink/prediction/kalman_predictor.h
@@ -36,7 +36,6 @@ // Generate the prediction based on stored points and given time_stamp. // Return false if no prediction available. bool GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const override; private:
diff --git a/ui/events/blink/prediction/kalman_predictor_unittest.cc b/ui/events/blink/prediction/kalman_predictor_unittest.cc index 039c6637..212fb7c 100644 --- a/ui/events/blink/prediction/kalman_predictor_unittest.cc +++ b/ui/events/blink/prediction/kalman_predictor_unittest.cc
@@ -122,10 +122,9 @@ if (filtered_predictor->HasPrediction() && predictor_->HasPrediction()) { ui::InputPredictor::InputData filtered_result; ui::InputPredictor::InputData unfiltered_result; - EXPECT_TRUE(filtered_predictor->GeneratePrediction( - FromMilliseconds(t[i]), false /* is_resampling */, &filtered_result)); + EXPECT_TRUE(filtered_predictor->GeneratePrediction(FromMilliseconds(t[i]), + &filtered_result)); EXPECT_TRUE(predictor_->GeneratePrediction(FromMilliseconds(t[i]), - false /* is_resampling */, &unfiltered_result)); EXPECT_LT(std::abs(filtered_result.pos.x() - x[i]), std::abs(unfiltered_result.pos.x() - x[i]));
diff --git a/ui/events/blink/prediction/least_squares_predictor.cc b/ui/events/blink/prediction/least_squares_predictor.cc index 786c141..1352fc8 100644 --- a/ui/events/blink/prediction/least_squares_predictor.cc +++ b/ui/events/blink/prediction/least_squares_predictor.cc
@@ -79,29 +79,21 @@ } bool LeastSquaresPredictor::GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const { if (!HasPrediction()) return false; - // For resampling, we don't want to predict too far away because the result - // will likely be inaccurate in that case. - if (is_resampling && predict_time - time_.back() > kMaxResampleTime) - return false; + float pred_dt = (predict_time - time_[0]).InMillisecondsF(); + gfx::Vector3dF b1, b2; gfx::Matrix3F time_matrix = GetXMatrix(); + if (SolveLeastSquares(time_matrix, x_queue_, b1) && + SolveLeastSquares(time_matrix, y_queue_, b2)) { + gfx::Vector3dF prediction_time(1, pred_dt, pred_dt * pred_dt); - double dt = (predict_time - time_[0]).InMillisecondsF(); - if (dt > 0) { - gfx::Vector3dF b1, b2; - if (SolveLeastSquares(time_matrix, x_queue_, b1) && - SolveLeastSquares(time_matrix, y_queue_, b2)) { - gfx::Vector3dF prediction_time(1, dt, dt * dt); - - result->pos.set_x(gfx::DotProduct(prediction_time, b1)); - result->pos.set_y(gfx::DotProduct(prediction_time, b2)); - return true; - } + result->pos.set_x(gfx::DotProduct(prediction_time, b1)); + result->pos.set_y(gfx::DotProduct(prediction_time, b2)); + return true; } return false; }
diff --git a/ui/events/blink/prediction/least_squares_predictor.h b/ui/events/blink/prediction/least_squares_predictor.h index b48ea23..87d734d1 100644 --- a/ui/events/blink/prediction/least_squares_predictor.h +++ b/ui/events/blink/prediction/least_squares_predictor.h
@@ -36,7 +36,6 @@ // Generate the prediction based on stored points and given time_stamp. // Return false if no prediction available. bool GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const override; private:
diff --git a/ui/events/blink/prediction/least_squares_predictor_unittest.cc b/ui/events/blink/prediction/least_squares_predictor_unittest.cc index 9415780..c708d25 100644 --- a/ui/events/blink/prediction/least_squares_predictor_unittest.cc +++ b/ui/events/blink/prediction/least_squares_predictor_unittest.cc
@@ -72,9 +72,10 @@ FromMilliseconds(t[i])}; predictor_->Update(data); } + // Expect false because the matrix is singular + // and the predictor cannot compute a prediction ui::InputPredictor::InputData result; - EXPECT_FALSE(predictor_->GeneratePrediction( - FromMilliseconds(42), false /* is_resampling */, &result)); + EXPECT_FALSE(predictor_->GeneratePrediction(FromMilliseconds(42), &result)); x = {100, 100, 100}; y = {100, 100, 100}; @@ -84,8 +85,7 @@ FromMilliseconds(t[i])}; predictor_->Update(data); } - EXPECT_FALSE(predictor_->GeneratePrediction( - FromMilliseconds(42), false /* is_resampling */, &result)); + EXPECT_TRUE(predictor_->GeneratePrediction(FromMilliseconds(142), &result)); } } // namespace test
diff --git a/ui/events/blink/prediction/linear_predictor.cc b/ui/events/blink/prediction/linear_predictor.cc index 9dd6911b..16f089b8 100644 --- a/ui/events/blink/prediction/linear_predictor.cc +++ b/ui/events/blink/prediction/linear_predictor.cc
@@ -78,7 +78,6 @@ } bool LinearPredictor::GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const { if (!HasPrediction()) return false; @@ -86,11 +85,6 @@ float pred_dt = (predict_time - events_queue_.back().time_stamp).InMillisecondsF(); - // For resampling, we don't want to predict too far away, - // We then take the maximum of prediction time - if (is_resampling) - pred_dt = std::fmax(pred_dt, kMaxResampleTime.InMillisecondsF()); - // Compute the prediction // Note : a first order prediction is computed when only 2 events are // available in the second order predictor
diff --git a/ui/events/blink/prediction/linear_predictor.h b/ui/events/blink/prediction/linear_predictor.h index 7ec49fd..9f15686 100644 --- a/ui/events/blink/prediction/linear_predictor.h +++ b/ui/events/blink/prediction/linear_predictor.h
@@ -40,7 +40,6 @@ // Generate the prediction based on stored points and given time_stamp. // Return false if no prediction available. bool GeneratePrediction(base::TimeTicks predict_time, - bool is_resampling, InputData* result) const override; // Return the number of events needed to compute a prediction
diff --git a/ui/events/blink/scroll_predictor.cc b/ui/events/blink/scroll_predictor.cc index df24ae692..3234f0d 100644 --- a/ui/events/blink/scroll_predictor.cc +++ b/ui/events/blink/scroll_predictor.cc
@@ -112,11 +112,26 @@ gfx::PointF predicted_accumulated_delta = current_accumulated_delta_; InputPredictor::InputData result; - if (predictor_->HasPrediction() && - predictor_->GeneratePrediction(time_stamp, true /* is_resampling */, - &result)) { - predicted_accumulated_delta = result.pos; - gesture_event->SetTimeStamp(time_stamp); + + base::TimeDelta prediction_delta = time_stamp - gesture_event->TimeStamp(); + + // Disable prediction when dt < 0. + if (prediction_delta > base::TimeDelta()) { + // For resampling, we don't want to predict too far away because the result + // will likely be inaccurate in that case. We cut off the prediction to the + // maximum available for the current predictor + prediction_delta = + std::min(prediction_delta, predictor_->MaxResampleTime()); + + // Compute the prediction timestamp + base::TimeTicks prediction_time = + gesture_event->TimeStamp() + prediction_delta; + + if (predictor_->HasPrediction() && + predictor_->GeneratePrediction(prediction_time, &result)) { + predicted_accumulated_delta = result.pos; + gesture_event->SetTimeStamp(prediction_time); + } } // If the last resampled GSU over predict the delta, new GSU might try to @@ -159,8 +174,7 @@ temporary_accumulated_delta_.Offset(gesture_event.data.scroll_update.delta_x, gesture_event.data.scroll_update.delta_y); if (predictor_->HasPrediction() && - predictor_->GeneratePrediction( - event->TimeStamp(), false /* is_resampling */, &predict_result)) { + predictor_->GeneratePrediction(event->TimeStamp(), &predict_result)) { float distance = (predict_result.pos - gfx::PointF(temporary_accumulated_delta_)) .Length();
diff --git a/ui/events/blink/scroll_predictor_unittest.cc b/ui/events/blink/scroll_predictor_unittest.cc index a38c192..679f7eb 100644 --- a/ui/events/blink/scroll_predictor_unittest.cc +++ b/ui/events/blink/scroll_predictor_unittest.cc
@@ -96,7 +96,7 @@ return scroll_predictor_->predictor_->GeneratePrediction( WebInputEvent::GetStaticTimeStampForTests() + base::TimeDelta::FromMillisecondsD(time_delta_in_milliseconds), - false /* is_resampling */, result); + result); } gfx::PointF GetLastAccumulatedDelta() {
diff --git a/ui/gl/ca_renderer_layer_params.cc b/ui/gl/ca_renderer_layer_params.cc index 73d1bde..b31832a 100644 --- a/ui/gl/ca_renderer_layer_params.cc +++ b/ui/gl/ca_renderer_layer_params.cc
@@ -6,21 +6,22 @@ namespace ui { -CARendererLayerParams::CARendererLayerParams(bool is_clipped, - const gfx::Rect clip_rect, - float clip_rect_corner_radius, - unsigned sorting_context_id, - const gfx::Transform& transform, - gl::GLImage* image, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter) +CARendererLayerParams::CARendererLayerParams( + bool is_clipped, + const gfx::Rect clip_rect, + const gfx::RRectF rounded_corner_bounds, + unsigned sorting_context_id, + const gfx::Transform& transform, + gl::GLImage* image, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity, + unsigned filter) : is_clipped(is_clipped), clip_rect(clip_rect), - clip_rect_corner_radius(clip_rect_corner_radius), + rounded_corner_bounds(rounded_corner_bounds), sorting_context_id(sorting_context_id), transform(transform), image(image),
diff --git a/ui/gl/ca_renderer_layer_params.h b/ui/gl/ca_renderer_layer_params.h index 83dd4b8b..d6ce053 100644 --- a/ui/gl/ca_renderer_layer_params.h +++ b/ui/gl/ca_renderer_layer_params.h
@@ -11,6 +11,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/rrect_f.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_export.h" @@ -23,7 +24,7 @@ struct GL_EXPORT CARendererLayerParams { CARendererLayerParams(bool is_clipped, const gfx::Rect clip_rect, - float clip_rect_corner_radius, + const gfx::RRectF rounded_corner_bounds, unsigned sorting_context_id, const gfx::Transform& transform, gl::GLImage* image, @@ -38,7 +39,7 @@ bool is_clipped; const gfx::Rect clip_rect; - float clip_rect_corner_radius; + const gfx::RRectF rounded_corner_bounds; unsigned sorting_context_id; const gfx::Transform transform; gl::GLImage* image;
diff --git a/ui/native_theme/native_theme_aura.cc b/ui/native_theme/native_theme_aura.cc index 878b85d39..817ad21 100644 --- a/ui/native_theme/native_theme_aura.cc +++ b/ui/native_theme/native_theme_aura.cc
@@ -87,6 +87,8 @@ const SkScalar kSliderThumbBorderWidth = 2.f; const SkScalar kSliderThumbBorderHoveredWidth = 4.f; +const SkScalar kMenuListArrowStrokeWidth = 1.f; + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -658,6 +660,58 @@ canvas->drawRoundRect(thumb_rect, radius, radius, flags); } +void NativeThemeAura::PaintMenuList( + cc::PaintCanvas* canvas, + State state, + const gfx::Rect& rect, + const MenuListExtraParams& menu_list) const { + if (!features::IsFormControlsRefreshEnabled()) { + return NativeThemeBase::PaintMenuList(canvas, state, rect, menu_list); + } + + // If a border radius is specified paint the background and the border of + // the menulist, otherwise let the non-theming code paint the background + // and the border of the control. The arrow (menulist button) is always + // painted by the theming code. + if (!menu_list.has_border_radius) { + TextFieldExtraParams text_field = {0}; + text_field.background_color = menu_list.background_color; + PaintTextField(canvas, state, rect, text_field); + } + + // Paint the arrow. + cc::PaintFlags flags; + flags.setColor(menu_list.arrow_color); + flags.setAntiAlias(true); + flags.setStyle(cc::PaintFlags::kStroke_Style); + flags.setStrokeWidth(kMenuListArrowStrokeWidth); + + float arrow_width = menu_list.arrow_size; + int arrow_height = arrow_width * 0.6; + gfx::Rect arrow(menu_list.arrow_x, menu_list.arrow_y - (arrow_height / 2), + arrow_width, arrow_height); + arrow.Intersect(rect); + + if (arrow_width != arrow.width() || arrow_height != arrow.height()) { + // The arrow is clipped after being constrained to the paint rect so we need + // to recalculate its size. + int height_clip = arrow_height - arrow.height(); + int width_clip = arrow_width - arrow.width(); + if (height_clip > width_clip) { + arrow.set_width(arrow.height() * 1.6); + } else { + arrow.set_height(arrow.width() * 0.6); + } + arrow.set_y(menu_list.arrow_y - (arrow.height() / 2)); + } + + SkPath path; + path.moveTo(arrow.x(), arrow.y()); + path.lineTo(arrow.x() + arrow.width() / 2, arrow.y() + arrow.height()); + path.lineTo(arrow.x() + arrow.width(), arrow.y()); + canvas->drawPath(path, flags); +} + gfx::Size NativeThemeAura::GetPartSize(Part part, State state, const ExtraParams& extra) const {
diff --git a/ui/native_theme/native_theme_aura.h b/ui/native_theme/native_theme_aura.h index 0d8f596..1217b87 100644 --- a/ui/native_theme/native_theme_aura.h +++ b/ui/native_theme/native_theme_aura.h
@@ -76,6 +76,10 @@ State state, const gfx::Rect& rect, const SliderExtraParams& slider) const override; + void PaintMenuList(cc::PaintCanvas* canvas, + State state, + const gfx::Rect& rect, + const MenuListExtraParams& menu_list) const override; gfx::Size GetPartSize(Part part, State state, const ExtraParams& extra) const override;
diff --git a/ui/views/animation/installable_ink_drop.cc b/ui/views/animation/installable_ink_drop.cc index e5a49ba..fa969ef 100644 --- a/ui/views/animation/installable_ink_drop.cc +++ b/ui/views/animation/installable_ink_drop.cc
@@ -37,7 +37,8 @@ event_handler_(view_, this), painter_(&visual_state_), animation_container_(base::MakeRefCounted<gfx::AnimationContainer>()), - animator_(&visual_state_, + animator_(layer_->size(), + &visual_state_, animation_container_.get(), base::Bind(&InstallableInkDrop::SchedulePaint, base::Unretained(this))) { @@ -83,6 +84,7 @@ void InstallableInkDrop::HostSizeChanged(const gfx::Size& new_size) { layer_->SetBounds(gfx::Rect(new_size) + layer_->bounds().OffsetFromOrigin()); layer_->SchedulePaint(gfx::Rect(layer_->size())); + animator_.SetSize(layer_->size()); } InkDropState InstallableInkDrop::GetTargetInkDropState() const { @@ -90,6 +92,11 @@ } void InstallableInkDrop::AnimateToState(InkDropState ink_drop_state) { + const gfx::Point ripple_center = + event_handler_.GetLastRippleTriggeringEvent() + ? event_handler_.GetLastRippleTriggeringEvent()->location() + : view_->GetMirroredRect(view_->GetLocalBounds()).CenterPoint(); + animator_.SetLastEventLocation(ripple_center); animator_.AnimateToState(ink_drop_state); }
diff --git a/ui/views/animation/installable_ink_drop_animator.cc b/ui/views/animation/installable_ink_drop_animator.cc index 391a598..0445cdf 100644 --- a/ui/views/animation/installable_ink_drop_animator.cc +++ b/ui/views/animation/installable_ink_drop_animator.cc
@@ -4,7 +4,17 @@ #include "ui/views/animation/installable_ink_drop_animator.h" +#include <algorithm> + #include "base/bind.h" +#include "base/logging.h" +#include "base/trace_event/trace_event.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/geometry/vector2d_f.h" #include "ui/views/animation/installable_ink_drop_painter.h" namespace views { @@ -13,53 +23,111 @@ constexpr base::TimeDelta InstallableInkDropAnimator::kAnimationDuration; InstallableInkDropAnimator::InstallableInkDropAnimator( + gfx::Size size, InstallableInkDropPainter::State* visual_state, gfx::AnimationContainer* animation_container, base::RepeatingClosure repaint_callback) - : visual_state_(visual_state), + : size_(size), + visual_state_(visual_state), repaint_callback_(repaint_callback), - highlight_animation_(this) { + highlight_animation_(this), + flood_fill_animation_(this), + fade_out_animation_(this) { highlight_animation_.SetContainer(animation_container); highlight_animation_.SetSlideDuration(kAnimationDuration.InMilliseconds()); + + flood_fill_animation_.SetContainer(animation_container); + flood_fill_animation_.SetDuration(kAnimationDuration); + + fade_out_animation_.SetContainer(animation_container); + fade_out_animation_.SetDuration(kAnimationDuration); } -InstallableInkDropAnimator::~InstallableInkDropAnimator() { - transition_delay_timer_.Stop(); +InstallableInkDropAnimator::~InstallableInkDropAnimator() = default; + +void InstallableInkDropAnimator::SetSize(gfx::Size size) { + size_ = size; +} + +void InstallableInkDropAnimator::SetLastEventLocation( + gfx::Point last_event_location) { + last_event_location_ = last_event_location; } void InstallableInkDropAnimator::AnimateToState(InkDropState target_state) { - transition_delay_timer_.Stop(); + VerifyAnimationState(); const InkDropState last_state = target_state_; + + TRACE_EVENT2("views", "InstallableInkDropAnimator::AnimateToState", + "target_state", target_state, "last_state", last_state); + switch (target_state) { case InkDropState::HIDDEN: case InkDropState::DEACTIVATED: - target_state_ = InkDropState::HIDDEN; - visual_state_->activated = false; - break; - case InkDropState::ACTION_PENDING: - case InkDropState::ALTERNATE_ACTION_PENDING: - case InkDropState::ACTIVATED: - target_state_ = target_state; - visual_state_->activated = true; - break; - case InkDropState::ACTION_TRIGGERED: - case InkDropState::ALTERNATE_ACTION_TRIGGERED: - if (last_state == InkDropState::ACTION_PENDING || - last_state == InkDropState::ALTERNATE_ACTION_PENDING) { - target_state_ = InkDropState::HIDDEN; - visual_state_->activated = false; - } else { - target_state_ = target_state; - visual_state_->activated = true; - transition_delay_timer_.Start( - FROM_HERE, kAnimationDuration, - base::Bind(&InstallableInkDropAnimator::AnimateToState, - base::Unretained(this), InkDropState::HIDDEN)); + // If we weren't animating to a visible state, we shouldn't do anything. + if (last_state == InkDropState::HIDDEN || + last_state == InkDropState::DEACTIVATED) { + break; } + // If we were flood-filling, jump to the end of that animation. + if (flood_fill_animation_.is_animating()) { + flood_fill_animation_.End(); + } + // If we weren't already fading out, start fading out. Otherwise, just + // continue fading out. + if (!fade_out_animation_.is_animating()) + fade_out_animation_.Start(); + break; + + case InkDropState::ACTION_PENDING: + if (last_state != InkDropState::HIDDEN) { + // Snap to hidden state by stopping all animations. + flood_fill_animation_.Stop(); + fade_out_animation_.Stop(); + } + flood_fill_animation_.Start(); + break; + + case InkDropState::ACTION_TRIGGERED: + if (last_state == InkDropState::HIDDEN) { + // Start the flood fill. On this animation's end, we will start the fade + // out animation in AnimationEnded(). + flood_fill_animation_.Start(); + } else if (last_state == InkDropState::ACTION_PENDING && + !flood_fill_animation_.is_animating()) { + // If we were done animating to ACTION_PENDING, we must start the fade + // out animation here. + fade_out_animation_.Start(); + // If we were in ACTION_PENDING but weren't done animating, we will + // start the fade out animation in AnimationEnded(). + } else if (last_state != InkDropState::ACTION_PENDING) { + // Any other state transitions are invalid. + NOTREACHED() << "Transition from " << ToString(last_state) + << " to ACTION_TRIGGERED is invalid."; + } + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state = " << ToString(target_state); + break; + + case InkDropState::ACTIVATED: + if (fade_out_animation_.is_animating()) { + // If we were fading out of ACTIVATED or ACTION_TRIGGERED, finish the + // animation to reset to HIDDEN state. + fade_out_animation_.End(); + } + + // Now simply start the flood fill animation again. + flood_fill_animation_.Start(); break; } + target_state_ = target_state; + VerifyAnimationState(); repaint_callback_.Run(); } @@ -71,9 +139,118 @@ } } +void InstallableInkDropAnimator::VerifyAnimationState() const { +#if DCHECK_IS_ON() + switch (target_state_) { + case InkDropState::HIDDEN: + case InkDropState::DEACTIVATED: + // We can only be fading out or completely invisible. So, we cannot be + // flood-filling. + DCHECK(!flood_fill_animation_.is_animating()); + break; + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // These states flood-fill then stay visible. + DCHECK(!fade_out_animation_.is_animating()); + if (!flood_fill_animation_.is_animating()) + DCHECK_EQ(1.0f, visual_state_->flood_fill_progress); + break; + case InkDropState::ACTION_TRIGGERED: + // We should always be animating during this state. Once animations are + // done, we automatically transition to HIDDEN. Make sure exactly one of + // our animations are running. + DCHECK_NE(flood_fill_animation_.is_animating(), + fade_out_animation_.is_animating()); + break; + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state = " << ToString(target_state_); + break; + } +#endif // DCHECK_IS_ON() +} + +void InstallableInkDropAnimator::AnimationEnded( + const gfx::Animation* animation) { + TRACE_EVENT0("views", "InstallableInkDropAnimator::AnimationEnded"); + + if (animation == &flood_fill_animation_) { + switch (target_state_) { + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // The ink drop stays filled in these states so nothing needs to be + // done. + break; + + case InkDropState::ACTION_TRIGGERED: + // After filling, the ink drop should fade out. + fade_out_animation_.Start(); + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + + case InkDropState::HIDDEN: + case InkDropState::DEACTIVATED: + // The flood fill animation should never run in these states. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + } + + // The ink drop is now fully activated. + visual_state_->flood_fill_progress = 1.0f; + repaint_callback_.Run(); + } else if (animation == &fade_out_animation_) { + switch (target_state_) { + case InkDropState::HIDDEN: + // Nothing to do. + break; + + case InkDropState::ACTION_TRIGGERED: + case InkDropState::DEACTIVATED: + // These states transition to HIDDEN when they're done animating. + target_state_ = InkDropState::HIDDEN; + break; + + case InkDropState::ACTION_PENDING: + case InkDropState::ACTIVATED: + // The fade out animation should never run in these states. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + + case InkDropState::ALTERNATE_ACTION_PENDING: + case InkDropState::ALTERNATE_ACTION_TRIGGERED: + // TODO(crbug.com/933384): handle these. + NOTREACHED() << "target_state_ = " << ToString(target_state_); + break; + } + + // The ink drop fill is fully invisible. + visual_state_->flood_fill_progress = 0.0f; + repaint_callback_.Run(); + } +} + void InstallableInkDropAnimator::AnimationProgressed( const gfx::Animation* animation) { - visual_state_->highlighted_ratio = highlight_animation_.GetCurrentValue(); + TRACE_EVENT0("views", "InstallableInkDropAnimator::AnimationProgressed"); + + if (animation == &highlight_animation_) { + visual_state_->highlighted_ratio = highlight_animation_.GetCurrentValue(); + } else if (animation == &flood_fill_animation_) { + visual_state_->flood_fill_center = gfx::PointF(last_event_location_); + visual_state_->flood_fill_progress = gfx::Tween::CalculateValue( + gfx::Tween::EASE_IN_OUT, flood_fill_animation_.GetCurrentValue()); + } else if (animation == &fade_out_animation_) { + // Do nothing for now. + } else { + NOTREACHED(); + } + repaint_callback_.Run(); }
diff --git a/ui/views/animation/installable_ink_drop_animator.h b/ui/views/animation/installable_ink_drop_animator.h index ea68cb8..b6bd896 100644 --- a/ui/views/animation/installable_ink_drop_animator.h +++ b/ui/views/animation/installable_ink_drop_animator.h
@@ -6,10 +6,14 @@ #define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_ANIMATOR_H_ #include "base/callback.h" +#include "base/optional.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "ui/gfx/animation/animation_delegate.h" +#include "ui/gfx/animation/linear_animation.h" #include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/animation/installable_ink_drop_painter.h" @@ -24,6 +28,9 @@ // InstallableInkDropPainter passed in. The animations are currently minimal. class VIEWS_EXPORT InstallableInkDropAnimator : public gfx::AnimationDelegate { public: + // Placeholder duration used for all animations. TODO(crbug.com/933384): + // remove this and replace it with separate durations for different + // animations. static constexpr base::TimeDelta kAnimationDuration = base::TimeDelta::FromMilliseconds(500); @@ -32,11 +39,15 @@ // |views::CompositorAnimationRunner| to be used for more efficient and less // janky animations, and it enables easier unit testing. explicit InstallableInkDropAnimator( + gfx::Size size, InstallableInkDropPainter::State* visual_state, gfx::AnimationContainer* animation_container, base::RepeatingClosure repaint_callback); ~InstallableInkDropAnimator() override; + void SetSize(gfx::Size size); + void SetLastEventLocation(gfx::Point last_event_location); + // Set the target state and animate to it. void AnimateToState(InkDropState target_state); @@ -47,9 +58,16 @@ InkDropState target_state() const { return target_state_; } private: + // Checks that the states of our animations make sense given + // |target_state_|. DCHECKs if something is wrong. + void VerifyAnimationState() const; + // gfx::AnimationDelegate: + void AnimationEnded(const gfx::Animation* animation) override; void AnimationProgressed(const gfx::Animation* animation) override; + gfx::Size size_; + // The visual state we are controlling. InstallableInkDropPainter::State* const visual_state_; @@ -61,7 +79,10 @@ // Used to animate the painter's highlight value in and out. gfx::SlideAnimation highlight_animation_; - base::OneShotTimer transition_delay_timer_; + gfx::LinearAnimation flood_fill_animation_; + gfx::LinearAnimation fade_out_animation_; + + gfx::Point last_event_location_; }; } // namespace views
diff --git a/ui/views/animation/installable_ink_drop_animator_unittest.cc b/ui/views/animation/installable_ink_drop_animator_unittest.cc index 9762409..d11209e 100644 --- a/ui/views/animation/installable_ink_drop_animator_unittest.cc +++ b/ui/views/animation/installable_ink_drop_animator_unittest.cc
@@ -10,28 +10,44 @@ #include "base/test/scoped_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/animation/animation_test_api.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/animation/installable_ink_drop_painter.h" namespace views { +namespace { + class InstallableInkDropAnimatorTest : public ::testing::Test { public: InstallableInkDropAnimatorTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME) {} + : animation_container_(base::MakeRefCounted<gfx::AnimationContainer>()), + animation_tester_(animation_container_.get()), + callback_( + base::Bind([](bool* callback_called) { *callback_called = true; }, + &callback_called_)) {} - base::test::ScopedTaskEnvironment* scoped_task_environment() { - return &scoped_task_environment_; - } - - private: + protected: base::test::ScopedTaskEnvironment scoped_task_environment_; + + scoped_refptr<gfx::AnimationContainer> animation_container_; + gfx::AnimationContainerTestApi animation_tester_; + + InstallableInkDropPainter::State visual_state_; + + bool callback_called_ = false; + base::RepeatingClosure callback_; }; +} // namespace + TEST_F(InstallableInkDropAnimatorTest, UpdatesTargetState) { - InstallableInkDropPainter::State visual_state; - InstallableInkDropAnimator animator(&visual_state, nullptr, + InstallableInkDropAnimator animator(gfx::Size(2, 2), &visual_state_, + animation_container_.get(), base::DoNothing()); EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); @@ -39,100 +55,213 @@ EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); } -TEST_F(InstallableInkDropAnimatorTest, InstantAnimationsFinishImmediately) { - InstallableInkDropPainter::State visual_state; +TEST_F(InstallableInkDropAnimatorTest, AnimateToTriggeredFromHidden) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); - bool callback_called = false; - base::RepeatingClosure callback = base::Bind( - [](bool* callback_called) { *callback_called = true; }, &callback_called); - InstallableInkDropAnimator animator(&visual_state, nullptr, callback); - EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); - EXPECT_FALSE(visual_state.activated); - EXPECT_FALSE(callback_called); - - animator.AnimateToState(InkDropState::ACTION_PENDING); - EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); - EXPECT_TRUE(visual_state.activated); - EXPECT_TRUE(callback_called); - - callback_called = false; + animator.SetLastEventLocation(gfx::Point(5, 5)); animator.AnimateToState(InkDropState::ACTION_TRIGGERED); - EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); - EXPECT_FALSE(visual_state.activated); - EXPECT_TRUE(callback_called); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); - callback_called = false; - animator.AnimateToState(InkDropState::ACTIVATED); - EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); - EXPECT_TRUE(visual_state.activated); - EXPECT_TRUE(callback_called); + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); - callback_called = false; - animator.AnimateToState(InkDropState::DEACTIVATED); + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); - EXPECT_FALSE(visual_state.activated); - EXPECT_TRUE(callback_called); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); } TEST_F(InstallableInkDropAnimatorTest, - TriggeredAnimationDelaysTransitionToHidden) { - InstallableInkDropPainter::State visual_state; + AnimateToPendingThenTriggeredFromHidden) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); - bool callback_called = false; - base::RepeatingClosure callback = base::Bind( - [](bool* callback_called) { *callback_called = true; }, &callback_called); - InstallableInkDropAnimator animator(&visual_state, nullptr, callback); - EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); - EXPECT_FALSE(visual_state.activated); - EXPECT_FALSE(callback_called); + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // The animation should be finished now and the visual state should *not* + // change; ACTION_PENDING lasts indefinitely. + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + // Animating to ACTION_TRIGGERED from ACTION_PENDING should not repeat the + // flood-fill animation. Instead, it should just fade out. animator.AnimateToState(InkDropState::ACTION_TRIGGERED); EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); - EXPECT_TRUE(visual_state.activated); - EXPECT_TRUE(callback_called); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); - callback_called = false; - scoped_task_environment()->FastForwardBy( + animation_tester_.IncrementTime( InstallableInkDropAnimator::kAnimationDuration); base::RunLoop().RunUntilIdle(); EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); - EXPECT_FALSE(visual_state.activated); - EXPECT_TRUE(callback_called); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); +} + +TEST_F(InstallableInkDropAnimatorTest, + AnimateToPendingWhileAnimatingToTriggered) { + const base::TimeDelta kHalfAnimationDuration = + InstallableInkDropAnimator::kAnimationDuration / 2; + const base::TimeDelta kRemainingAnimationDuration = + InstallableInkDropAnimator::kAnimationDuration - kHalfAnimationDuration; + + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTION_PENDING); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kHalfAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_PENDING, animator.target_state()); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // Switching to ACTION_TRIGGERED should not restart the animation. Instead, it + // should just add a transition to HIDDEN after the flood-fill is done. + animator.AnimateToState(InkDropState::ACTION_TRIGGERED); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kRemainingAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTION_TRIGGERED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); +} + +TEST_F(InstallableInkDropAnimatorTest, AnimateToActivatedThenDeactivated) { + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(gfx::Point(5, 5)); + animator.AnimateToState(InkDropState::ACTIVATED); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + EXPECT_TRUE(callback_called_); + + // The state should stay the same indefinitely. + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::ACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + // Animating to DEACTIVATED should fade out and transition to HIDDEN. + animator.AnimateToState(InkDropState::DEACTIVATED); + EXPECT_EQ(InkDropState::DEACTIVATED, animator.target_state()); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime( + InstallableInkDropAnimator::kAnimationDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(InkDropState::HIDDEN, animator.target_state()); + EXPECT_EQ(0.0f, visual_state_.flood_fill_progress); +} + +TEST_F(InstallableInkDropAnimatorTest, + FloodFillAnimationExpandsFromEventLocation) { + constexpr gfx::Point kEventLocation(3, 7); + + // Split |InstallableInkDrop::kAnimationDuration| into three chunks. + const base::TimeDelta kFirstDuration = + InstallableInkDropAnimator::kAnimationDuration / 3; + const base::TimeDelta kSecondDuration = kFirstDuration; + const base::TimeDelta kLastDuration = + InstallableInkDropAnimator::kAnimationDuration - kFirstDuration - + kSecondDuration; + + InstallableInkDropAnimator animator(gfx::Size(10, 10), &visual_state_, + animation_container_.get(), callback_); + + animator.SetLastEventLocation(kEventLocation); + animator.AnimateToState(InkDropState::ACTIVATED); + + callback_called_ = false; + animation_tester_.IncrementTime(kFirstDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_LT(0.0f, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + const float first_progress = visual_state_.flood_fill_progress; + + callback_called_ = false; + animation_tester_.IncrementTime(kSecondDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_LT(first_progress, visual_state_.flood_fill_progress); + EXPECT_GT(1.0f, visual_state_.flood_fill_progress); + + callback_called_ = false; + animation_tester_.IncrementTime(kLastDuration); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(1.0f, visual_state_.flood_fill_progress); } TEST_F(InstallableInkDropAnimatorTest, HighlightAnimationFadesInAndOut) { - InstallableInkDropPainter::State visual_state; - - auto animation_container = base::MakeRefCounted<gfx::AnimationContainer>(); - gfx::AnimationContainerTestApi animation_test_api(animation_container.get()); - - bool callback_called = false; - base::RepeatingClosure callback = base::Bind( - [](bool* callback_called) { *callback_called = true; }, &callback_called); - InstallableInkDropAnimator animator(&visual_state, animation_container.get(), - callback); - EXPECT_EQ(0.0f, visual_state.highlighted_ratio); - EXPECT_FALSE(callback_called); + InstallableInkDropAnimator animator(gfx::Size(2, 2), &visual_state_, + animation_container_.get(), callback_); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); + EXPECT_FALSE(callback_called_); animator.AnimateHighlight(true); - EXPECT_EQ(0.0f, visual_state.highlighted_ratio); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); - callback_called = false; - animation_test_api.IncrementTime( + callback_called_ = false; + animation_tester_.IncrementTime( InstallableInkDropAnimator::kAnimationDuration); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1.0f, visual_state.highlighted_ratio); - EXPECT_TRUE(callback_called); + EXPECT_EQ(1.0f, visual_state_.highlighted_ratio); + EXPECT_TRUE(callback_called_); animator.AnimateHighlight(false); - EXPECT_EQ(1.0f, visual_state.highlighted_ratio); + EXPECT_EQ(1.0f, visual_state_.highlighted_ratio); - callback_called = false; - animation_test_api.IncrementTime( + callback_called_ = false; + animation_tester_.IncrementTime( InstallableInkDropAnimator::kAnimationDuration); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(0.0f, visual_state.highlighted_ratio); - EXPECT_TRUE(callback_called); + EXPECT_EQ(0.0f, visual_state_.highlighted_ratio); + EXPECT_TRUE(callback_called_); } } // namespace views
diff --git a/ui/views/animation/installable_ink_drop_painter.cc b/ui/views/animation/installable_ink_drop_painter.cc index 0e88beb6..7157ba5 100644 --- a/ui/views/animation/installable_ink_drop_painter.cc +++ b/ui/views/animation/installable_ink_drop_painter.cc
@@ -4,9 +4,16 @@ #include "ui/views/animation/installable_ink_drop_painter.h" +#include "base/trace_event/trace_event.h" +#include "cc/paint/paint_flags.h" +#include "ui/gfx/animation/tween.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/vector2d_f.h" namespace { // Placeholder colors and alphas. TODO(crbug.com/933384): get rid of @@ -19,24 +26,56 @@ namespace views { +InstallableInkDropPainter::State::State() = default; +InstallableInkDropPainter::State::~State() = default; + gfx::Size InstallableInkDropPainter::GetMinimumSize() const { return gfx::Size(); } void InstallableInkDropPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { - if (state_->activated) { + TRACE_EVENT0("views", "InstallableInkDropPainter::Paint"); + + DCHECK_GE(state_->flood_fill_progress, 0.0f); + DCHECK_LE(state_->flood_fill_progress, 1.0f); + DCHECK_GE(state_->highlighted_ratio, 0.0f); + DCHECK_LE(state_->highlighted_ratio, 1.0f); + + // If fully filled, there is no need to draw the highlight, and we can draw + // the activated color more efficiently as a rectangle. + if (state_->flood_fill_progress == 1.0f) { canvas->FillRect( gfx::Rect(size), SkColorSetA(kInstallableInkDropBaseColor, kInstallableInkDropActivatedOpacity * SK_AlphaOPAQUE)); - } else if (state_->highlighted_ratio > 0.0f) { + return; + } + + if (state_->highlighted_ratio > 0.0f) { canvas->FillRect( gfx::Rect(size), SkColorSetA(kInstallableInkDropBaseColor, kInstallableInkDropHighlightedOpacity * state_->highlighted_ratio * SK_AlphaOPAQUE)); } + + if (state_->flood_fill_progress > 0.0f) { + // We interpolate between a circle of radius 2 and a circle whose radius is + // the diagonal of |size|. + const float min_radius = 2.0f; + const float max_radius = + gfx::Vector2dF(size.width(), size.height()).Length(); + const float cur_radius = gfx::Tween::FloatValueBetween( + state_->flood_fill_progress, min_radius, max_radius); + + cc::PaintFlags flags; + flags.setStyle(cc::PaintFlags::kFill_Style); + flags.setColor( + SkColorSetA(kInstallableInkDropBaseColor, + kInstallableInkDropActivatedOpacity * SK_AlphaOPAQUE)); + canvas->DrawCircle(state_->flood_fill_center, cur_radius, flags); + } } } // namespace views
diff --git a/ui/views/animation/installable_ink_drop_painter.h b/ui/views/animation/installable_ink_drop_painter.h index 0b66e33..8337836 100644 --- a/ui/views/animation/installable_ink_drop_painter.h +++ b/ui/views/animation/installable_ink_drop_painter.h
@@ -5,6 +5,8 @@ #ifndef UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_PAINTER_H_ #define UI_VIEWS_ANIMATION_INSTALLABLE_INK_DROP_PAINTER_H_ +#include "base/optional.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/views/painter.h" namespace views { @@ -15,9 +17,13 @@ // |gfx::Canvas::ClipPath()| to control the shape. class VIEWS_EXPORT InstallableInkDropPainter : public Painter { public: - struct State { + struct VIEWS_EXPORT State { + State(); + ~State(); + + gfx::PointF flood_fill_center; + float flood_fill_progress = 0.0f; float highlighted_ratio = 0.0f; - bool activated = false; }; // |state| must outlive |this|.
diff --git a/ui/views/animation/installable_ink_drop_unittest.cc b/ui/views/animation/installable_ink_drop_unittest.cc index 0968bc9..b6d27e2e 100644 --- a/ui/views/animation/installable_ink_drop_unittest.cc +++ b/ui/views/animation/installable_ink_drop_unittest.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/test/scoped_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/gfx/geometry/rect.h" @@ -26,6 +27,8 @@ View* root_view() { return &root_view_; } private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + View root_view_; };
diff --git a/ui/webui/resources/cr_elements/cr_page_host_style_css.html b/ui/webui/resources/cr_elements/cr_page_host_style_css.html new file mode 100644 index 0000000..f3fadca --- /dev/null +++ b/ui/webui/resources/cr_elements/cr_page_host_style_css.html
@@ -0,0 +1,22 @@ +<link rel="import" href="../html/polymer.html"> + +<link rel="import" href="shared_vars_css.html"> + +<!-- + Common CSS properties for WebUI pages, such as an entire page or a standalone + dialog. The CSS here is in its own file so that the properties can be imported + independently and applied directly to the :host element without having to + import other shared CSS. +--> +<dom-module id="cr-page-host-style"> + <template> + <style> + :host { + color: var(--cr-primary-text); + line-height: 154%; /* Apply 20px default line-height to all text. */ + overflow: hidden; /* Prevent double scroll bar bugs. */ + user-select: text; + } + </style> + </template> +</dom-module>
diff --git a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html index 0354f71..963c6c8 100644 --- a/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html +++ b/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html
@@ -59,7 +59,6 @@ padding: 0 24px; text-align: start; width: 100%; - @apply --settings-actionable; } .list-item:focus {
diff --git a/ui/webui/resources/cr_elements/shared_style_css.html b/ui/webui/resources/cr_elements/shared_style_css.html index 292378f..36fd8e0 100644 --- a/ui/webui/resources/cr_elements/shared_style_css.html +++ b/ui/webui/resources/cr_elements/shared_style_css.html
@@ -56,10 +56,6 @@ [scrollable].can-scroll:not(.scrolled-to-bottom) { border-bottom-color: var(--scrollable-border-color); } - [scrollable] iron-list > :not(.no-outline):focus { - @apply --cr-list-item-focus; - @apply --cr-selectable-focus; - } .scroll-container { display: flex; @@ -68,8 +64,10 @@ } [selectable]:focus, - [selectable] > :focus { - @apply --cr-selectable-focus; + [selectable] > :focus, + [scrollable] iron-list > :not(.no-outline):focus { + background-color: var(--cr-focused-item-color); + outline: none; } [selectable] > * { cursor: pointer;
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html index 3a17d537..a8bcf13 100644 --- a/ui/webui/resources/cr_elements/shared_vars_css.html +++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -136,14 +136,6 @@ --cr-icon-ripple-margin: calc(var(--cr-icon-ripple-padding) * -1); - /* Common properties for WebUI pages. */ - --cr-page-host: { - color: var(--cr-primary-text-color); - line-height: 154%; /* Apply 20px default line-height to all text. */ - overflow: hidden; /* Prevent double scroll bar bugs. */ - user-select: text; - } - --cr-paper-icon-button-margin: { /* Shift button so ripple overlaps the end of the row. */ margin-inline-end: var(--cr-icon-ripple-margin); @@ -205,10 +197,6 @@ padding: 10px 8px; } - --cr-selectable-focus: { - background-color: var(--cr-focused-item-color); - outline: none; - } --cr-separator-height: 1px; --cr-separator-line: var(--cr-separator-height) solid var(--cr-separator-color);
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp index 15d93b19..aac8598 100644 --- a/ui/webui/resources/cr_elements_resources.grdp +++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -379,11 +379,15 @@ file="cr_elements/hidden_style_css.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_CR_ELEMENTS_CR_CR_ICONS_CSS_HTML" + <structure name="IDR_CR_ELEMENTS_CR_ICONS_CSS_HTML" file="cr_elements/cr_icons_css.html" flattenhtml="true" type="chrome_html" compress="gzip" /> + <structure name="IDR_CR_ELEMENTS_CR_PAGE_HOST_STYLE_CSS_HTML" + file="cr_elements/cr_page_host_style_css.html" + type="chrome_html" + compress="gzip" /> <structure name="IDR_CR_ELEMENTS_MD_SELECT_CSS_HTML" file="cr_elements/md_select_css.html" type="chrome_html"